codevdesign 1.0.42 → 1.0.44

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.
@@ -1,586 +1,831 @@
1
- <template>
2
- <csqc-modale
3
- ref="modale"
4
- :titre="$tc('csqc-table.choixColonnes.titre')"
5
- :afficher-ok="false"
6
- :afficher-annuler="!sauvegardeEnCours"
7
- width="90vw"
8
- max-width="4000px"
9
- @fermer="fermer"
10
- >
11
- <v-alert
12
- v-model="afficherErreur"
13
- type="error"
14
- closable
15
- >{{ erreur }}</v-alert
16
- >
17
- <v-row>
18
- <v-col
19
- cols="12"
20
- class="pa-0 relative"
21
- >
22
- <v-btn
23
- v-show="choixRetourEnHaut"
24
- class="BarreRechercheBackIcone retourHautChoix"
25
- location="bottom right"
26
- absolute
27
- color="primary"
28
- @click="retourEnHaut('#choixColonnes-choix > .v-data-table__wrapper')"
29
- >
30
- <v-icon>mdi-arrow-up</v-icon>
31
- </v-btn>
32
- <v-data-table
33
- id="choixColonnes-choix"
34
- v-model:expanded="choixEnCours"
35
- class="limiteHauteurChoix"
36
- :headers="colonnesChoix"
37
- item-key="nomVue"
38
- :items="choix"
39
- single-expand
40
- disable-pagination
41
- hide-default-footer
42
- fixed-header
43
- @click:row="cliquer"
44
- >
45
- <template #header.action>
46
- <v-btn
47
- color="primary"
48
- class="float-right"
49
- size="small"
50
- :disabled="desactiverAjout"
51
- @click.stop="ajouter"
52
- >
53
- {{ $tc('csqc-table.button.ajouter') }}
54
- </v-btn>
55
- </template>
56
-
57
- <template #expanded-item="{ headers }">
58
- <td
59
- :colspan="headers.length"
60
- class="pa-0 ma-0"
61
- >
62
- <v-row
63
- class="pa-0 ma-0"
64
- no-gutters
65
- >
66
- <v-col
67
- cols="12"
68
- class="pa-0 ma-0"
69
- >
70
- <v-btn
71
- v-show="colonnesRetourEnHaut"
72
- class="BarreRechercheBackIcone retourHautChoix colonnes"
73
- location="bottom right"
74
- absolute
75
- color="primary"
76
- @click="retourEnHaut('#choixColonnes-vue > .v-data-table__wrapper')"
77
- >
78
- <v-icon>mdi-arrow-up</v-icon>
79
- </v-btn>
80
- <v-data-table
81
- id="choixColonnes-vue"
82
- v-model:sort-by="triColonnesChoix"
83
- v-sortable-data-table
84
- class="limiteHauteurChoix colonnes mt-1 mb-4 ml-2 ordonable"
85
- :headers="colonnesChoixColonne"
86
- :items="colonnesEnCours"
87
- item-key="value"
88
- hide-default-footer
89
- disable-pagination
90
- fixed-header
91
- :style="`margin-right: ${90 - $vuetify.breakpoint.scrollBarWidth}px;`"
92
- @sorted="changeOrdre"
93
- >
94
- <template
95
- v-if="choixEnCours[0]?.colonnes.length <= 0"
96
- #header.action
97
- >
98
- <v-tooltip
99
- location="left"
100
- color="warning"
101
- >
102
- <template #activator="{ props }">
103
- <v-icon
104
- color="warning"
105
- v-bind="props"
106
- >mdi-alert</v-icon
107
- >
108
- </template>
109
- {{ $tc('csqc-table.choixColonnes.activezUneColonne') }}
110
- </v-tooltip>
111
- </template>
112
-
113
- <template #item.action="{ item }">
114
- <v-btn
115
- class="mr-1"
116
- icon
117
- @click.stop="basculeColonneClique(item)"
118
- >
119
- <v-icon :color="couleurColonneCliquee(item)">{{
120
- colonneEstClique(item) ? 'mdi-eye' : 'mdi-eye-off'
121
- }}</v-icon>
122
- </v-btn>
123
- </template>
124
- <template #item.text="{ item }">{{ item.text ? item.text : item.value }}</template>
125
- </v-data-table>
126
- </v-col>
127
- </v-row>
128
- </td>
129
- </template>
130
-
131
- <template #item.nomVue="{ item }">
132
- <v-text-field
133
- v-if="item.nomVue === vueEnEdition"
134
- v-model.trim="nomVueEnCours"
135
- :label="$tc('csqc-table.choixColonnes.nomVue')"
136
- single-line
137
- density="compact"
138
- autofocus
139
- class="py-0 my-0"
140
- :rules="regles.nomVue"
141
- hide-details
142
- @keydown.enter="accepterEdition"
143
- @keydown.esc.stop="annulerEdition"
144
- ></v-text-field>
145
- <span
146
- v-else
147
- :class="{ 'error--text': !item.nomVue }"
148
- >
149
- {{ item.nomVue || $tc('csqc-table.choixColonnes.nomVueRequis') }}
150
- </span>
151
- </template>
152
- <template #item.action="{ item }">
153
- <template v-if="item.nomVue === vueEnEdition">
154
- <v-btn
155
- icon
156
- @click.stop.prevent="annulerEdition"
157
- ><v-icon class="iconeSupprimer">mdi-window-close</v-icon></v-btn
158
- >
159
- <v-btn
160
- icon
161
- @click.stop.prevent="accepterEdition"
162
- ><v-icon class="couleurHover">mdi-check</v-icon></v-btn
163
- >
164
- </template>
165
- <template v-else>
166
- <v-btn
167
- icon
168
- @click.stop.prevent="selectionner(item)"
169
- ><v-icon class="couleurHover">mdi-eye</v-icon></v-btn
170
- >
171
- <v-btn
172
- icon
173
- @click.stop.prevent="mettreDefaut(item)"
174
- >
175
- <v-icon
176
- class="couleurHover"
177
- :color="couleurDefaut(item)"
178
- >mdi-star</v-icon
179
- >
180
- </v-btn>
181
- <v-btn
182
- icon
183
- @click.stop.prevent="editer(item)"
184
- ><v-icon class="couleurHover">mdi-pencil</v-icon></v-btn
185
- >
186
- <v-btn
187
- icon
188
- @click.stop.prevent="supprimer(item)"
189
- ><v-icon class="iconeSupprimer">mdi-delete</v-icon></v-btn
190
- >
191
- </template>
192
- </template>
193
- </v-data-table>
194
- </v-col>
195
- </v-row>
196
-
197
- <template #actions>
198
- <v-btn
199
- color="primary"
200
- class="elevation-0"
201
- :disabled="!formValide"
202
- :loading="sauvegardeEnCours"
203
- @click.stop="sauvegarder"
204
- >
205
- {{ $tc('csqc-table.choixColonnes.ok') }}
206
- </v-btn>
207
- </template>
208
- </csqc-modale>
209
- </template>
210
-
211
- <script>
212
- import Sortable from 'sortablejs'
213
- import axios from '@/outils/appAxios'
214
- import CsqcModale from '../CsqcDialogue/csqc-boite-dialogue.vue'
215
-
216
- export default {
217
- directives: {
218
- sortableDataTable: {
219
- bind(el, binding, vnode) {
220
- const options = {
221
- animation: 150,
222
- onUpdate(event) {
223
- vnode.child.$emit('sorted', event)
224
- },
225
- }
226
- Sortable.create(el.getElementsByTagName('tbody')[0], options)
227
- },
228
- },
229
- },
230
- components: {
231
- 'csqc-modale': CsqcModale,
232
- },
233
- props: {
234
- urlbase: {
235
- required: true,
236
- type: String,
237
- },
238
- formulaireId: {
239
- required: true,
240
- type: Number,
241
- },
242
- identifiant: {
243
- required: true,
244
- type: String,
245
- },
246
- colonnes: {
247
- required: true,
248
- type: Array,
249
- },
250
- choixOrigine: {
251
- required: true,
252
- type: Array,
253
- },
254
- },
255
- data() {
256
- return {
257
- erreur: '',
258
- afficherErreur: false,
259
- choixEnCours: [],
260
- choix: [],
261
- colonnesEnCours: [],
262
- triColonnesChoix: ['ordre'],
263
- sauvegardeEnCours: false,
264
- choixRetourEnHaut: false,
265
- colonnesRetourEnHaut: false,
266
- nomVueEnCours: '',
267
- vueEnEdition: '',
268
- desactiverAjout: false,
269
- regles: {
270
- nomVue: [
271
- v => (v && v.trim().length >= 1) || this.$tc('csqc-table.choixColonnes.nomVueRequis'),
272
- v =>
273
- !this.choix.some(({ nomVue }) => v.trim() === nomVue) ||
274
- this.$tc('csqc-table.choixColonnes.nomVueExiste'),
275
- ],
276
- },
277
- }
278
- },
279
-
280
- computed: {
281
- colonnesCliquees() {
282
- if (this.choixEnCours.length <= 0) return []
283
-
284
- const retour = this.colonnesEnCours
285
- .filter(c => this.choixEnCours[0].colonnes.some(x => x === c.value))
286
- .map(c => c.value)
287
- return retour
288
- },
289
- colonnesChoixColonne() {
290
- const cols = [
291
- {
292
- text: this.$tc('csqc-table.choixColonnes.ordre'),
293
- align: 'start',
294
- sortable: false,
295
- value: 'ordre',
296
- width: '5%',
297
- },
298
- {
299
- text: this.$tc('csqc-table.choixColonnes.nomColonne', this.colonnesEnCours.length),
300
- align: 'start',
301
- sortable: false,
302
- value: 'text',
303
- width: '85%',
304
- },
305
- {
306
- value: 'action',
307
- sortable: false,
308
- width: '10%',
309
- align: 'right',
310
- },
311
- ]
312
- return cols
313
- },
314
- colonnesChoix() {
315
- return [
316
- {
317
- text: this.$tc('csqc-table.choixColonnes.vues', this.choix.length),
318
- value: 'nomVue',
319
- width: '70%',
320
- align: 'start',
321
- },
322
- {
323
- value: 'action',
324
- width: '30%',
325
- align: 'right',
326
- sortable: false,
327
- },
328
- ]
329
- },
330
- formValide() {
331
- return !this.choix.some(vue => vue.nomVue === '' || vue.colonnes.length <= 0)
332
- },
333
- },
334
-
335
- watch: {
336
- erreur() {
337
- this.afficherErreur = this.erreur !== ''
338
- },
339
- afficherErreur() {
340
- if (this.afficherErreur === false) {
341
- this.erreur = ''
342
- }
343
- },
344
- },
345
- methods: {
346
- ouvrir() {
347
- this.choix = JSON.parse(JSON.stringify(this.choixOrigine))
348
- this.$refs.modale.ouvrir()
349
- this.ecouteDefilerChoix()
350
- },
351
-
352
- fermer() {
353
- this.sauvegardeEnCours = false
354
- this.desactiverAjout = false
355
- this.vueEnEdition = ''
356
- this.choixEnCours = []
357
- this.colonnesEnCours = []
358
- this.erreur = ''
359
- this.$refs.modale.fermer()
360
- },
361
-
362
- ajouter() {
363
- const ch = {
364
- nomVue: '',
365
- colonnes: [],
366
- defaut: this.choix.length === 0,
367
- }
368
- this.choix.push(ch)
369
-
370
- this.cliquer(ch)
371
- this.editer(ch)
372
- },
373
- editer(ch) {
374
- this.desactiverAjout = true
375
- if (ch.nomVue !== '') this.supprimer({ nomVue: '' })
376
-
377
- this.erreur = ''
378
- this.nomVueEnCours = ch.nomVue
379
- this.vueEnEdition = ch.nomVue
380
- },
381
- accepterEdition() {
382
- if (!this.nomVueEnCours) {
383
- this.erreur = this.$tc('csqc-table.choixColonnes.nomVueRequis')
384
- return
385
- }
386
-
387
- if (this.choix.some(({ nomVue }) => nomVue === this.nomVueEnCours)) {
388
- this.erreur = this.$tc('csqc-table.choixColonnes.nomVueExiste')
389
- return
390
- }
391
-
392
- const ch = this.choix.find(({ nomVue }) => nomVue === this.vueEnEdition)
393
- const choixClique = this.choixEstClique(ch)
394
-
395
- ch.nomVue = this.nomVueEnCours
396
- if (choixClique) this.choixEnCours = [ch]
397
- this.vueEnEdition = ''
398
- this.nomVueEnCours = ''
399
- this.desactiverAjout = false
400
- },
401
- annulerEdition() {
402
- const ch = this.choix.find(({ nomVue }) => nomVue === this.vueEnEdition)
403
- if (ch.nomVue === '') this.supprimer(ch)
404
- this.vueEnEdition = ''
405
- this.nomVueEnCours = ''
406
- this.desactiverAjout = false
407
- },
408
-
409
- selectionner(ch) {
410
- this.$emit('selection', ch)
411
- this.fermer()
412
- },
413
-
414
- cliquer(ch) {
415
- if (this.choixEnCours.length > 0) this.retourEnHaut('#choixColonnes-vue > .v-data-table__wrapper')
416
- if (this.choixEstClique(ch)) {
417
- this.choixEnCours = []
418
- this.colonnesEnCours = []
419
- return
420
- }
421
-
422
- this.colonnesEnCours = JSON.parse(JSON.stringify(this.colonnes)) // Toujours commencer avec les colonnes par défaut
423
- this.choixEnCours = [ch]
424
- const tempCols = []
425
- const colsPlusDispo = []
426
-
427
- this.choixEnCours[0].colonnes.forEach(colonne => {
428
- const tCol = this.colonnesEnCours.find(c => c.value === colonne)
429
- if (tCol == null)
430
- colsPlusDispo.push(colonne) // Ne supprime pas dans la collection lors son itération
431
- else tempCols.push(tCol)
432
- })
433
-
434
- // Nettoyer la vue au besoin (effectif seulement si l'utilisateur sauve les choix)
435
- if (colsPlusDispo.length > 0) {
436
- this.choixEnCours[0].colonnes = this.choixEnCours[0].colonnes.filter(
437
- c => !colsPlusDispo.some(cpd => c === cpd),
438
- )
439
- }
440
-
441
- this.colonnesEnCours = tempCols.concat(
442
- this.colonnesEnCours.filter(c => !this.choixEnCours[0].colonnes.some(cch => cch === c.value)),
443
- )
444
- this.calculOrdreColonnes()
445
- this.ecouteDefilerColonnes()
446
- },
447
-
448
- choixEstClique(ch) {
449
- return ch.nomVue === this.choixEnCours[0]?.nomVue
450
- },
451
-
452
- mettreDefaut(ch) {
453
- this.choix.find(({ defaut }) => defaut).defaut = false
454
- this.choix.find(({ nomVue }) => nomVue === ch.nomVue).defaut = true
455
- },
456
- couleurDefaut(ch) {
457
- if (ch.defaut === true) {
458
- return '#daa520'
459
- }
460
-
461
- return 'gray'
462
- },
463
-
464
- colonneEstClique(colonne) {
465
- if (this.choixEnCours.length <= 0) {
466
- return false
467
- }
468
-
469
- return this.choixEnCours[0].colonnes.some(x => x === colonne.value)
470
- },
471
- couleurColonneCliquee(colonne) {
472
- if (this.colonneEstClique(colonne)) return 'primary'
473
- return 'gray'
474
- },
475
- basculeColonneClique(colonne) {
476
- if (this.colonneEstClique(colonne)) {
477
- this.choixEnCours[0].colonnes = this.choixEnCours[0].colonnes.filter(x => x !== colonne.value)
478
- return
479
- }
480
-
481
- this.choixEnCours[0].colonnes.push(colonne.value)
482
- },
483
-
484
- changeOrdre(event) {
485
- const movedItem = this.colonnesEnCours.splice(event.oldIndex, 1)[0]
486
- this.colonnesEnCours.splice(event.newIndex, 0, movedItem)
487
- this.calculOrdreColonnes()
488
- this.choixEnCours[0].colonnes = this.colonnesCliquees
489
- },
490
- calculOrdreColonnes() {
491
- for (let i = 0; i < this.colonnesEnCours.length; i += 1) {
492
- this.colonnesEnCours[i].ordre = 1 + i
493
- }
494
- },
495
-
496
- supprimer(ch) {
497
- if (this.choixEstClique(ch)) this.choixEnCours = []
498
-
499
- const index = this.choix.findIndex(({ nomVue }) => nomVue === ch.nomVue)
500
- if (index === -1) {
501
- return
502
- }
503
-
504
- if (this.choix[index].defaut) {
505
- const nonDefaut = this.choix.find(({ nomVue }) => nomVue !== ch.nomVue)
506
- if (nonDefaut) {
507
- nonDefaut.defaut = true
508
- }
509
- }
510
-
511
- this.choix.splice(index, 1)
512
- },
513
-
514
- // ENREGISTREMENT
515
- sauvegarder() {
516
- axios
517
- .post(`${this.urlbase}/api/ComposantUI/Colonnes/${this.formulaireId}/Identifiant/${this.identifiant}`, {
518
- valeur: JSON.stringify(this.choix),
519
- })
520
- .then(({ data }) => {
521
- this.choix = JSON.parse(data.resultat)
522
- this.$emit('sauvegarde', this.choix)
523
- if (this.choixEnCours.length > 0) this.$emit('selection', this.choixEnCours[0])
524
- this.fermer()
525
- })
526
- .catch(e => {
527
- this.erreur = e
528
- })
529
- },
530
-
531
- retourEnHaut(cible) {
532
- this.$vuetify.goTo(0, { container: cible })
533
- },
534
- defilerChoix(e) {
535
- this.choixRetourEnHaut = e.target.scrollTop >= 200
536
- },
537
- ecouteDefilerChoix() {
538
- const vdtwChoix = document.querySelector('#choixColonnes-choix > .v-data-table__wrapper')
539
- if (vdtwChoix == null) {
540
- setTimeout(this.ecouteDefilerChoix.bind(this), 100)
541
- return
542
- }
543
- vdtwChoix.addEventListener('scroll', this.defilerChoix.bind(this))
544
- },
545
- defilerColonnes(e) {
546
- this.colonnesRetourEnHaut = e.target.scrollTop >= 200
547
- },
548
- ecouteDefilerColonnes() {
549
- const vdtwCols = document.querySelector('#choixColonnes-vue > .v-data-table__wrapper')
550
- if (vdtwCols == null) {
551
- setTimeout(this.ecouteDefilerColonnes.bind(this), 100)
552
- return
553
- }
554
- vdtwCols.addEventListener('scroll', this.defilerColonnes.bind(this))
555
- },
556
- },
557
- }
558
- </script>
559
- <style scoped>
560
- .relative {
561
- position: relative;
562
- }
563
- .retourHautChoix {
564
- bottom: 4px !important;
565
- right: 4px;
566
- }
567
- .retourHautChoix.colonnes {
568
- bottom: 8px !important;
569
- right: 48px;
570
- }
571
- </style>
572
- <style>
573
- .limiteHauteurChoix > .v-data-table__wrapper {
574
- max-height: min(80vh - 200px, 900px);
575
- overflow-y: auto;
576
- }
577
- #choixColonnes-choix > .v-data-table__wrapper > table > thead > tr > th {
578
- z-index: 3;
579
- }
580
- .limiteHauteurChoix.colonnes > .v-data-table__wrapper {
581
- max-height: min(80vh - 294px, 600px);
582
- }
583
- .v-data-table.ordonable > .v-data-table__wrapper > table > tbody > tr {
584
- cursor: move;
585
- }
586
- </style>
1
+ <template>
2
+ <csqc-modale
3
+ ref="modale"
4
+ :titre="t('csqc.csqcDatatable.choixColonnes.titre')"
5
+ :btn-annuler="!sauvegardeEnCours"
6
+ :largeur="'80vw'"
7
+ :max-width="'1000px'"
8
+ @annuler="fermer"
9
+ @ok="sauvegarder"
10
+ >
11
+ <!-- Erreurs -->
12
+ <v-alert
13
+ v-model="afficherErreur"
14
+ type="error"
15
+ closable
16
+ >
17
+ {{ erreur }}
18
+ </v-alert>
19
+
20
+ <v-row>
21
+ <v-col
22
+ cols="12"
23
+ class="pa-0 relative"
24
+ >
25
+ <!-- Retour en haut (table des vues) -->
26
+ <v-btn
27
+ v-show="choixRetourEnHaut"
28
+ class="BarreRechercheBackIcone retourHautChoix"
29
+ icon
30
+ color="primary"
31
+ style="position: absolute"
32
+ @click="retourEnHaut('#choixColonnes-choix > .v-data-table__wrapper')"
33
+ >
34
+ <v-icon>mdi-arrow-up</v-icon>
35
+ </v-btn>
36
+
37
+ <v-card
38
+ rounded
39
+ class="pr-0"
40
+ >
41
+ <!-- TABLE 1 : Liste des vues -->
42
+ <v-data-table
43
+ id="choixColonnes-choix"
44
+ class="limiteHauteurChoix"
45
+ :headers="colonnesChoix"
46
+ :items="choix"
47
+ :item-value="row => choixOf(row)._id"
48
+ v-model:expanded="expandedChoixIds"
49
+ show-expand
50
+ :single-expand="true"
51
+ :items-per-page="-1"
52
+ hide-default-footer
53
+ fixed-header
54
+ @click:row="onClickRowChoix"
55
+ >
56
+ <!-- Bouton ajouter une vue -->
57
+ <template #header.action>
58
+ <v-btn
59
+ color="primary"
60
+ class="float-right"
61
+ size="small"
62
+ :disabled="desactiverAjout"
63
+ @click.stop="ajouter"
64
+ >
65
+ {{ t('csqc.bouton.ajouter') }}
66
+ </v-btn>
67
+ </template>
68
+ <template v-slot:item.data-table-expand="{ internalItem, isExpanded, toggleExpand }">
69
+ <v-btn
70
+ :append-icon="isExpanded(internalItem) ? 'mdi-chevron-up' : 'mdi-chevron-down'"
71
+ :text="isExpanded(internalItem) ? 'Fermer' : 'Ouvrir'"
72
+ class="text-none"
73
+ color="medium-emphasis"
74
+ size="small"
75
+ variant="text"
76
+ width="105"
77
+ border
78
+ slim
79
+ data-expander
80
+ @click.stop="() => toggleExpand(internalItem)"
81
+ ></v-btn>
82
+ </template>
83
+ <!-- Expansion : Table des colonnes -->
84
+ <template #expanded-row="{ columns }">
85
+ <tr>
86
+ <td
87
+ :colspan="columns.length"
88
+ class="pa-0 ma-0"
89
+ >
90
+ <v-row
91
+ class="pa-0 ma-0"
92
+ no-gutters
93
+ >
94
+ <v-col
95
+ cols="12"
96
+ class="pa-0 ma-0"
97
+ >
98
+ <!-- Retour en haut (table des colonnes) -->
99
+ <v-btn
100
+ v-show="colonnesRetourEnHaut"
101
+ class="BarreRechercheBackIcone retourHautChoix colonnes"
102
+ icon
103
+ color="primary"
104
+ style="position: absolute"
105
+ @click="retourEnHaut('#choixColonnes-vue > .v-data-table__wrapper')"
106
+ >
107
+ <v-icon>mdi-arrow-up</v-icon>
108
+ </v-btn>
109
+
110
+ <!-- TABLE 2 : Colonnes de la vue sélectionnée -->
111
+ <v-data-table
112
+ id="choixColonnes-vue"
113
+ class="limiteHauteurChoix colonnes mt-1 mb-4 ordonable"
114
+ :headers="colonnesChoixColonne"
115
+ :items="colonnesEnCours"
116
+ hover
117
+ item-value="value"
118
+ hide-default-footer
119
+ :items-per-page="-1"
120
+ fixed-header
121
+ :sort-by="triColonnesChoix"
122
+ v-sortable-data-table
123
+ @sorted="changeOrdre"
124
+ >
125
+ <!-- Alerte dans l'entête si aucune colonne n'est sélectionnée -->
126
+ <template #header.action>
127
+ <v-tooltip
128
+ v-if="nbColonnesSelectionnees <= 0"
129
+ location="start"
130
+ color="warning"
131
+ >
132
+ <template #activator="{ props }">
133
+ <v-icon
134
+ color="warning"
135
+ v-bind="props"
136
+ >
137
+ mdi-alert
138
+ </v-icon>
139
+ </template>
140
+ {{ t('csqc.csqcDatatable.choixColonnes.activezUneColonne') }}
141
+ </v-tooltip>
142
+ </template>
143
+
144
+ <!-- Toggle (oeil) -->
145
+ <template #item.action="{ item }">
146
+ <v-icon
147
+ :color="couleurColonneCliquee(colonneOf(item))"
148
+ @click.stop="basculeColonneClique(colonneOf(item))"
149
+ >
150
+ {{ colonneEstClique(colonneOf(item)) ? 'mdi-eye' : 'mdi-eye-off' }}
151
+ </v-icon>
152
+ </template>
153
+
154
+ <!-- Label de colonne -->
155
+ <template #item.title="{ value, item }">
156
+ {{ value ?? colonneOf(item).value }}
157
+ </template>
158
+ </v-data-table>
159
+ </v-col>
160
+ </v-row>
161
+ </td>
162
+ </tr>
163
+ </template>
164
+
165
+ <!-- Nom de vue (édition inline) -->
166
+ <template #item.nomVue="{ item }">
167
+ <v-text-field
168
+ v-if="choixOf(item).nomVue === vueEnEdition"
169
+ v-model.trim="nomVueEnCours"
170
+ :label="t('csqc.csqcDatatable.choixColonnes.nomVue')"
171
+ single-line
172
+ density="compact"
173
+ autofocus
174
+ class="py-0 my-0"
175
+ :rules="regles.nomVue"
176
+ hide-details
177
+ @keydown.enter="accepterEdition"
178
+ @keydown.esc.stop="annulerEdition"
179
+ />
180
+ <span
181
+ v-else
182
+ :class="{ 'error--text': !choixOf(item).nomVue }"
183
+ >
184
+ {{ choixOf(item).nomVue || t('csqc.csqcDatatable.choixColonnes.nomVueRequis') }}
185
+ </span>
186
+ </template>
187
+
188
+ <!-- Actions (vue) -->
189
+ <template #item.action="{ item }">
190
+ <template v-if="choixOf(item).nomVue === vueEnEdition">
191
+ <v-icon
192
+ class="iconeSupprimer GouttiereSmall"
193
+ @click.stop.prevent="annulerEdition"
194
+ >
195
+ mdi-window-close
196
+ </v-icon>
197
+
198
+ <v-icon
199
+ class="iconeHover GouttiereSmall"
200
+ @click.stop.prevent="accepterEdition"
201
+ >
202
+ mdi-check
203
+ </v-icon>
204
+ </template>
205
+
206
+ <template v-else>
207
+ <v-tooltip
208
+ location="start"
209
+ color="warning"
210
+ >
211
+ <template #activator="{ props }">
212
+ <v-icon
213
+ class="iconeHover GouttiereSmall"
214
+ v-bind="props"
215
+ @click.stop.prevent="selectionner(choixOf(item))"
216
+ >
217
+ mdi-eye
218
+ </v-icon>
219
+ </template>
220
+ {{ t('csqc.csqcDatatable.choixColonnes.activerTooltip') }}
221
+ </v-tooltip>
222
+ <v-tooltip
223
+ location="start"
224
+ color="warning"
225
+ >
226
+ <template #activator="{ props }">
227
+ <v-icon
228
+ v-bind="props"
229
+ class="iconeHover GouttiereSmall"
230
+ :color="couleurDefaut(choixOf(item))"
231
+ @click.stop.prevent="mettreDefaut(choixOf(item))"
232
+ >
233
+ mdi-star
234
+ </v-icon>
235
+ </template>
236
+ {{ t('csqc.csqcDatatable.choixColonnes.defautTooltip') }}
237
+ </v-tooltip>
238
+ <!-- <v-icon
239
+ class="iconeHover GouttiereSmall"
240
+ @click.stop.prevent="editer(choixOf(item))"
241
+ >
242
+ mdi-pencil
243
+ </v-icon> -->
244
+
245
+ <v-icon
246
+ class="iconeSupprimer GouttiereSmall"
247
+ @click.stop.prevent="supprimer(choixOf(item))"
248
+ >
249
+ mdi-delete
250
+ </v-icon>
251
+ </template>
252
+ </template>
253
+ </v-data-table>
254
+ </v-card>
255
+ </v-col>
256
+ </v-row>
257
+ </csqc-modale>
258
+ </template>
259
+
260
+ <script setup lang="ts">
261
+ /* eslint-disable @typescript-eslint/no-explicit-any */
262
+
263
+ /**
264
+ * csqcTableModaleChoixColonnes.vue
265
+ * - Permet de créer/éditer des "vues" de colonnes (ensembles de colonnes visibles)
266
+ * - Drag & drop pour réordonner les colonnes
267
+ * - Persistance via endpoint ComposantUI/Colonnes/...
268
+ */
269
+
270
+ import axios from '../../outils/appAxios'
271
+ import Sortable from 'sortablejs'
272
+ import { computed, nextTick, reactive, ref, watch } from 'vue'
273
+ import { useGoTo } from 'vuetify'
274
+ import { useI18n } from 'vue-i18n'
275
+ import CsqcModale from '../csqcDialogue.vue'
276
+
277
+ type SortableEvent = import('sortablejs').SortableEvent
278
+
279
+ // ─────────────────────────────────────────────────────────────────────────────
280
+ // Types
281
+ // ─────────────────────────────────────────────────────────────────────────────
282
+ type ColonneChoix = {
283
+ value: string
284
+ text?: string
285
+ ordre?: number
286
+ }
287
+
288
+ type Colonne = {
289
+ value: string
290
+ title?: string
291
+ ordre?: number
292
+ }
293
+
294
+ type ChoixVue = {
295
+ nomVue: string
296
+ colonnes: string[]
297
+ defaut: boolean
298
+ _id: string // interne (clé stable si nomVue est vide)
299
+ }
300
+
301
+ type DataTableItemLike<T> = { raw: T }
302
+
303
+ // ─────────────────────────────────────────────────────────────────────────────
304
+ // Helpers "unwrap" (évite item.raw dans le template + évite génériques TS dans template)
305
+ // ─────────────────────────────────────────────────────────────────────────────
306
+ function asRaw<T>(item: T | DataTableItemLike<T>): T {
307
+ return (item as any)?.raw ?? (item as T)
308
+ }
309
+
310
+ const choixOf = (x: any) => asRaw<ChoixVue>(x)
311
+ const colonneOf = (x: any) => asRaw<Colonne>(x)
312
+
313
+ // Génère des ids pour stabiliser l’expansion / l’édition même si nomVue = ''
314
+ function assurerIds(vues: ChoixVue[]) {
315
+ for (const v of vues) {
316
+ if (!v._id) v._id = crypto.randomUUID()
317
+ }
318
+ }
319
+
320
+ // Clone profond simple (OK ici car données sérialisables)
321
+ function deepClone<T>(x: T): T {
322
+ return JSON.parse(JSON.stringify(x)) as T
323
+ }
324
+
325
+ // ─────────────────────────────────────────────────────────────────────────────
326
+ // Props / Emits / Expose
327
+ // ─────────────────────────────────────────────────────────────────────────────
328
+ type ModaleExpose = { ouvrir: () => void; fermer: () => void }
329
+
330
+ const props = defineProps<{
331
+ urlbase: string
332
+ formulaireId: number
333
+ identifiant: string
334
+ colonnes: ColonneChoix[]
335
+ choixOrigine: ChoixVue[]
336
+ }>()
337
+
338
+ const emit = defineEmits<{
339
+ (e: 'selection', choix: ChoixVue): void
340
+ (e: 'sauvegarde', choix: ChoixVue[]): void
341
+ }>()
342
+
343
+ const modale = ref<ModaleExpose | null>(null)
344
+
345
+ defineExpose({ ouvrir, fermer })
346
+
347
+ // ─────────────────────────────────────────────────────────────────────────────
348
+ // Directive: drag & drop (sortablejs)
349
+ // ─────────────────────────────────────────────────────────────────────────────
350
+ defineOptions({
351
+ directives: {
352
+ sortableDataTable: {
353
+ mounted(el: HTMLElement, _binding: any, vnode: any) {
354
+ const tbody = el.getElementsByTagName('tbody')?.[0]
355
+ if (!tbody) return
356
+
357
+ Sortable.create(tbody, {
358
+ animation: 150,
359
+ onUpdate(event: SortableEvent) {
360
+ if (event.oldIndex == null || event.newIndex == null) return
361
+ vnode?.component?.emit?.('sorted', event)
362
+ },
363
+ })
364
+ },
365
+ },
366
+ },
367
+ })
368
+
369
+ // ─────────────────────────────────────────────────────────────────────────────
370
+ // UI / i18n / scroll helpers
371
+ // ─────────────────────────────────────────────────────────────────────────────
372
+ const { t } = useI18n()
373
+ const goTo = useGoTo()
374
+
375
+ const erreur = ref('')
376
+ const afficherErreur = ref(false)
377
+
378
+ const choixRetourEnHaut = ref(false)
379
+ const colonnesRetourEnHaut = ref(false)
380
+
381
+ // ─────────────────────────────────────────────────────────────────────────────
382
+ // State
383
+ // ─────────────────────────────────────────────────────────────────────────────
384
+ const choix = ref<ChoixVue[]>([])
385
+ const choixEnCours = ref<ChoixVue | null>(null)
386
+ const colonnesEnCours = ref<Colonne[]>([])
387
+
388
+ const sauvegardeEnCours = ref(false)
389
+
390
+ // Edition du nom de vue
391
+ const nomVueEnCours = ref('')
392
+ const vueEnEdition = ref('')
393
+ const desactiverAjout = ref(false)
394
+
395
+ // Tri visuel (on veut ordre asc)
396
+ const triColonnesChoix = ref<Array<{ key: string; order?: 'asc' | 'desc' }>>([{ key: 'ordre', order: 'asc' }])
397
+
398
+ // Règles validation
399
+ const regles = reactive({
400
+ nomVue: [
401
+ (v: string) => (v && v.trim().length >= 1) || t('csqc.csqcDatatable.choixColonnes.nomVueRequis'),
402
+ (v: string) =>
403
+ !choix.value.some(({ nomVue }) => v.trim() === nomVue) || t('csqc.csqcDatatable.choixColonnes.nomVueExiste'),
404
+ ],
405
+ })
406
+
407
+ // Affiche l'alerte si aucune colonne sélectionnée dans la vue courante
408
+ const nbColonnesSelectionnees = computed(() => choixEnCours.value?.colonnes?.length ?? 0)
409
+
410
+ function ouvrirVue(ch: ChoixVue) {
411
+ // remonte en haut la table des colonnes quand on change
412
+ if (choixEnCours.value) retourEnHaut('#choixColonnes-vue > .v-data-table__wrapper')
413
+
414
+ // Build colonnesEnCours à partir des colonnes disponibles
415
+ colonnesEnCours.value = deepClone(props.colonnes)
416
+
417
+ // Référence réactive dans la liste
418
+ const refDansListe = choix.value.find(x => x._id === ch._id) ?? ch
419
+ choixEnCours.value = refDansListe
420
+
421
+ // Colonnes sélectionnées d'abord, puis le reste
422
+ const selected: Colonne[] = []
423
+ const missing: string[] = []
424
+
425
+ for (const key of choixEnCours.value.colonnes) {
426
+ const col = colonnesEnCours.value.find(c => c.value === key)
427
+ if (!col) missing.push(key)
428
+ else selected.push(col)
429
+ }
430
+
431
+ // Nettoyage des colonnes disparues
432
+ if (missing.length) {
433
+ choixEnCours.value.colonnes = choixEnCours.value.colonnes.filter(k => !missing.includes(k))
434
+ }
435
+
436
+ // selected puis disponibles non sélectionnées
437
+ colonnesEnCours.value = selected.concat(
438
+ colonnesEnCours.value.filter(c => !choixEnCours.value!.colonnes.includes(c.value)),
439
+ )
440
+
441
+ calculOrdreColonnes()
442
+ ecouteDefilerColonnes()
443
+ }
444
+
445
+ const expandedChoixIds = ref<string[]>([])
446
+
447
+ // colonnes
448
+ const colonnesChoixColonne = computed(() => [
449
+ {
450
+ title: t('csqc.csqcDatatable.choixColonnes.ordre'),
451
+ key: 'ordre',
452
+ align: 'start' as const,
453
+ sortable: false,
454
+ width: '5%',
455
+ },
456
+ {
457
+ title: t('csqc.csqcDatatable.choixColonnes.nomColonne', colonnesEnCours.value.length),
458
+ key: 'title',
459
+ align: 'start' as const,
460
+ sortable: false,
461
+ width: '85%',
462
+ },
463
+ { title: '', key: 'action', align: 'end' as const, sortable: false, width: '10%' },
464
+ ])
465
+
466
+ const colonnesChoix = computed(() => [
467
+ {
468
+ title: t('csqc.csqcDatatable.choixColonnes.vues', choix.value.length),
469
+ key: 'nomVue',
470
+ width: '70%',
471
+ align: 'start' as const,
472
+ },
473
+ { title: '', key: 'action', width: '30%', align: 'end' as const, sortable: false },
474
+ ])
475
+
476
+ // ─────────────────────────────────────────────────────────────────────────────
477
+ // actions
478
+ // ─────────────────────────────────────────────────────────────────────────────
479
+ function ouvrir() {
480
+ choix.value = deepClone(props.choixOrigine)
481
+ assurerIds(choix.value) // obligatoire
482
+ expandedChoixIds.value = [] // reset
483
+ modale.value?.ouvrir()
484
+ }
485
+ function fermer() {
486
+ // Reset UI
487
+ sauvegardeEnCours.value = false
488
+ desactiverAjout.value = false
489
+ vueEnEdition.value = ''
490
+ nomVueEnCours.value = ''
491
+
492
+ // Reset sélection
493
+ choixEnCours.value = null
494
+ colonnesEnCours.value = []
495
+
496
+ // Reset erreurs
497
+ afficherErreur.value = false
498
+ erreur.value = ''
499
+
500
+ modale.value?.fermer()
501
+ }
502
+
503
+ // ─────────────────────────────────────────────────────────────────────────────
504
+ // Actions : vues
505
+ // ─────────────────────────────────────────────────────────────────────────────
506
+ function ajouter() {
507
+ const ch: ChoixVue = {
508
+ _id: crypto.randomUUID(),
509
+ nomVue: '',
510
+ colonnes: [],
511
+ defaut: choix.value.length === 0,
512
+ }
513
+ choix.value.push(ch)
514
+ expandedChoixIds.value = [ch._id] // ouvre celle-là
515
+ editer(ch)
516
+ }
517
+
518
+ function editer(ch: ChoixVue) {
519
+ desactiverAjout.value = true
520
+
521
+ // Nettoyage d’un placeholder vide précédent (si on relance edit)
522
+ if (ch.nomVue !== '') supprimer({ _id: 'placeholder', nomVue: '', colonnes: [], defaut: false })
523
+
524
+ erreur.value = ''
525
+ nomVueEnCours.value = ch.nomVue
526
+ vueEnEdition.value = ch.nomVue
527
+ }
528
+
529
+ function accepterEdition() {
530
+ const nom = nomVueEnCours.value?.trim()
531
+
532
+ if (!nom) {
533
+ erreur.value = t('csqc.csqcDatatable.choixColonnes.nomVueRequis')
534
+ return
535
+ }
536
+
537
+ if (choix.value.some(({ nomVue }) => nomVue === nom)) {
538
+ erreur.value = t('csqc.csqcDatatable.choixColonnes.nomVueExiste')
539
+ return
540
+ }
541
+
542
+ const ch = choix.value.find(({ nomVue }) => nomVue === vueEnEdition.value)
543
+ if (!ch) return
544
+
545
+ const etaitSelectionnee = choixEstClique(ch)
546
+ ch.nomVue = nom
547
+
548
+ // Si c’était la vue active, on s’assure que la ref active reste valide
549
+ if (etaitSelectionnee) {
550
+ choixEnCours.value = ch
551
+ }
552
+
553
+ // Reset édition
554
+ vueEnEdition.value = ''
555
+ nomVueEnCours.value = ''
556
+ desactiverAjout.value = false
557
+ }
558
+
559
+ function annulerEdition() {
560
+ const ch = choix.value.find(({ nomVue }) => nomVue === vueEnEdition.value)
561
+ if (!ch) return
562
+
563
+ // Si c'était une nouvelle vue vide, on la retire
564
+ if (ch.nomVue === '') supprimer(ch)
565
+
566
+ vueEnEdition.value = ''
567
+ nomVueEnCours.value = ''
568
+ desactiverAjout.value = false
569
+ }
570
+
571
+ function selectionner(ch: ChoixVue) {
572
+ emit('selection', ch)
573
+ fermer()
574
+ }
575
+
576
+ function supprimer(ch: ChoixVue) {
577
+ if (choixEstClique(ch)) choixEnCours.value = null
578
+
579
+ const index = choix.value.findIndex(({ nomVue }) => nomVue === ch.nomVue)
580
+ if (index === -1) return
581
+
582
+ // Si on supprime le défaut, on transfère le défaut ailleurs
583
+ if (choix.value[index].defaut) {
584
+ const autre = choix.value.find(({ nomVue }) => nomVue !== ch.nomVue)
585
+ if (autre) autre.defaut = true
586
+ }
587
+
588
+ choix.value.splice(index, 1)
589
+ }
590
+
591
+ function mettreDefaut(ch: ChoixVue) {
592
+ const actuel = choix.value.find(v => v.defaut)
593
+ if (actuel) actuel.defaut = false
594
+
595
+ const cible = choix.value.find(v => v.nomVue === ch.nomVue)
596
+ if (cible) cible.defaut = true
597
+ }
598
+
599
+ function couleurDefaut(ch: ChoixVue) {
600
+ return ch.defaut ? '#daa520' : 'gray'
601
+ }
602
+
603
+ function choixEstClique(ch: ChoixVue) {
604
+ return ch.nomVue === choixEnCours.value?.nomVue
605
+ }
606
+
607
+ // Click row Vuetify (payload = { item })
608
+ function onClickRowChoix(_e: Event, payload: any) {
609
+ cliquer(choixOf(payload?.item ?? payload))
610
+ }
611
+
612
+ /**
613
+ * Sélection d’une vue :
614
+ * - recalcul de colonnesEnCours (selected d'abord, puis le reste)
615
+ * - nettoyage des colonnes "non disponibles" (si props.colonnes a changé)
616
+ */
617
+ function cliquer(ch: ChoixVue) {
618
+ const id = ch._id
619
+ if (!id) return
620
+
621
+ expandedChoixIds.value = expandedChoixIds.value[0] === id ? [] : [id]
622
+ }
623
+
624
+ // ─────────────────────────────────────────────────────────────────────────────
625
+ // Actions : colonnes
626
+ // ─────────────────────────────────────────────────────────────────────────────
627
+ function colonneEstClique(colonne: Colonne) {
628
+ if (!choixEnCours.value) return false
629
+ return choixEnCours.value.colonnes.includes(colonne.value)
630
+ }
631
+
632
+ function couleurColonneCliquee(colonne: Colonne) {
633
+ return colonneEstClique(colonne) ? 'primary' : 'gray'
634
+ }
635
+
636
+ function basculeColonneClique(colonne: Colonne) {
637
+ if (!choixEnCours.value) return
638
+
639
+ if (colonneEstClique(colonne)) {
640
+ choixEnCours.value.colonnes = choixEnCours.value.colonnes.filter(x => x !== colonne.value)
641
+ return
642
+ }
643
+
644
+ choixEnCours.value.colonnes.push(colonne.value)
645
+ }
646
+
647
+ // Liste des colonnes actuellement "cliquées" selon l’ordre affiché
648
+ const colonnesCliquees = computed(() => {
649
+ if (!choixEnCours.value) return []
650
+ return colonnesEnCours.value.filter(c => choixEnCours.value!.colonnes.includes(c.value)).map(c => c.value)
651
+ })
652
+
653
+ function changeOrdre(event: SortableEvent) {
654
+ if (event.oldIndex == null || event.newIndex == null) return
655
+
656
+ const moved = colonnesEnCours.value.splice(event.oldIndex, 1)[0]
657
+ colonnesEnCours.value.splice(event.newIndex, 0, moved)
658
+
659
+ calculOrdreColonnes()
660
+
661
+ // Après reorder, on sauvegarde la sélection dans le nouvel ordre visuel
662
+ if (choixEnCours.value) {
663
+ choixEnCours.value.colonnes = colonnesCliquees.value
664
+ }
665
+ }
666
+
667
+ function calculOrdreColonnes() {
668
+ for (let i = 0; i < colonnesEnCours.value.length; i += 1) {
669
+ colonnesEnCours.value[i].ordre = i + 1
670
+ }
671
+ }
672
+
673
+ // ─────────────────────────────────────────────────────────────────────────────
674
+ // axios
675
+ // ─────────────────────────────────────────────────────────────────────────────
676
+ async function sauvegarder() {
677
+ sauvegardeEnCours.value = true
678
+
679
+ try {
680
+ const res: any = await axios
681
+ .getAxios()
682
+ .post(`${props.urlbase}/api/ComposantUI/Colonnes/${props.formulaireId}/Identifiant/${props.identifiant}`, {
683
+ valeur: JSON.stringify(choix.value),
684
+ })
685
+
686
+ const payload = res?.data ?? res
687
+
688
+ // payload peut être un json string ou déjà un tableau
689
+ choix.value = typeof payload === 'string' ? (JSON.parse(payload) as ChoixVue[]) : (payload as ChoixVue[])
690
+
691
+ assurerIds(choix.value)
692
+
693
+ // Notifie le parent
694
+ emit('sauvegarde', choix.value)
695
+ if (choixEnCours.value) emit('selection', choixEnCours.value)
696
+
697
+ fermer()
698
+ } catch (e) {
699
+ erreur.value = String(e)
700
+ } finally {
701
+ sauvegardeEnCours.value = false
702
+ }
703
+ }
704
+
705
+ // ─────────────────────────────────────────────────────────────────────────────
706
+ // Scroll helpers
707
+ // ─────────────────────────────────────────────────────────────────────────────
708
+ function retourEnHaut(cible: string) {
709
+ goTo(0, { container: cible })
710
+ }
711
+
712
+ function defilerChoix(e: Event) {
713
+ const target = e.target as HTMLElement
714
+ choixRetourEnHaut.value = target.scrollTop >= 200
715
+ }
716
+
717
+ function ecouteDefilerChoix() {
718
+ const el = document.querySelector('#choixColonnes-choix > .v-data-table__wrapper')
719
+ if (!el) {
720
+ setTimeout(ecouteDefilerChoix, 100)
721
+ return
722
+ }
723
+ el.addEventListener('scroll', defilerChoix)
724
+ }
725
+
726
+ function defilerColonnes(e: Event) {
727
+ const target = e.target as HTMLElement
728
+ colonnesRetourEnHaut.value = target.scrollTop >= 200
729
+ }
730
+
731
+ function ecouteDefilerColonnes() {
732
+ nextTick(() => {
733
+ const el = document.querySelector('#choixColonnes-vue > .v-data-table__wrapper')
734
+ if (!el) {
735
+ setTimeout(ecouteDefilerColonnes, 100)
736
+ return
737
+ }
738
+ el.addEventListener('scroll', defilerColonnes)
739
+ })
740
+ }
741
+
742
+ // ─────────────────────────────────────────────────────────────────────────────
743
+ // Watchers
744
+ // ─────────────────────────────────────────────────────────────────────────────
745
+ watch(erreur, () => {
746
+ afficherErreur.value = erreur.value !== ''
747
+ })
748
+
749
+ watch(afficherErreur, v => {
750
+ if (v === false) erreur.value = ''
751
+ })
752
+
753
+ watch(
754
+ expandedChoixIds,
755
+ v => {
756
+ const id = v?.[0]
757
+
758
+ // Fermeture
759
+ if (!id) {
760
+ choixEnCours.value = null
761
+ colonnesEnCours.value = []
762
+ return
763
+ }
764
+
765
+ // Ouverture / changement
766
+ const ch = choix.value.find(x => x._id === id)
767
+ if (!ch) return
768
+
769
+ ouvrirVue(ch) // IMPORTANT: ouverture "force", pas toggle
770
+ },
771
+ { flush: 'post' }, // <-- clé du fix
772
+ )
773
+ watch(
774
+ expandedChoixIds,
775
+ v => {
776
+ if (v.length > 1) expandedChoixIds.value = [v[v.length - 1]] // garde le dernier
777
+ },
778
+ { flush: 'sync' },
779
+ )
780
+ </script>
781
+
782
+ <style scoped>
783
+ /* Layout */
784
+ .relative {
785
+ position: relative;
786
+ }
787
+
788
+ /* Boutons "retour en haut" */
789
+ .retourHautChoix {
790
+ bottom: 4px !important;
791
+ right: 4px;
792
+ }
793
+ .retourHautChoix.colonnes {
794
+ bottom: 8px !important;
795
+ right: 48px;
796
+ }
797
+
798
+ /* Scroll vertical des tables */
799
+ .limiteHauteurChoix > .v-data-table__wrapper {
800
+ max-height: min(80vh - 200px, 900px);
801
+ overflow-y: auto;
802
+ }
803
+ .limiteHauteurChoix.colonnes > .v-data-table__wrapper {
804
+ max-height: min(80vh - 294px, 600px);
805
+ }
806
+
807
+ /* Z-index du header table 1 */
808
+ #choixColonnes-choix > .v-data-table__wrapper > table > thead > tr > th {
809
+ z-index: 3;
810
+ }
811
+
812
+ /* Drag cursor */
813
+ .v-data-table.ordonable > .v-data-table__wrapper > table > tbody > tr {
814
+ cursor: move;
815
+ }
816
+
817
+ /* Icons */
818
+ .iconeSupprimer:hover {
819
+ color: red;
820
+ }
821
+ .v-icon.v-icon.v-icon--link.iconeSupprimer:hover {
822
+ color: red !important;
823
+ }
824
+ .iconeEditer:hover {
825
+ color: #095797;
826
+ }
827
+
828
+ .GouttiereSmall {
829
+ margin-left: 8px;
830
+ }
831
+ </style>