codevdesign 2.0.3 → 2.0.4
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/composants/csqcCodeBudgetaireGenerique.vue +340 -340
- package/composants/csqcSwitch.vue +220 -220
- package/composants/csqcTable/csqcTable.vue +2 -2
- package/composants/csqcTexteBilingue.vue +175 -175
- package/composants/csqcTiroir.vue +197 -197
- package/package.json +1 -1
|
@@ -1,220 +1,220 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<v-row
|
|
3
|
-
density="compact"
|
|
4
|
-
class="align-center"
|
|
5
|
-
>
|
|
6
|
-
<!-- Texte + détails -->
|
|
7
|
-
<v-col
|
|
8
|
-
cols="10"
|
|
9
|
-
xl="11"
|
|
10
|
-
class="py-0"
|
|
11
|
-
>
|
|
12
|
-
<component
|
|
13
|
-
:is="labelCliquable ? 'label' : 'div'"
|
|
14
|
-
:for="labelCliquable ? switchId : undefined"
|
|
15
|
-
class="labelSwitchSiSwitchApres"
|
|
16
|
-
:class="{ 'label-cliquable': labelCliquable && !desactiver }"
|
|
17
|
-
>
|
|
18
|
-
<div class="label-ligne">
|
|
19
|
-
<div class="label-gauche">
|
|
20
|
-
<slot name="label">
|
|
21
|
-
{{ texte }}
|
|
22
|
-
</slot>
|
|
23
|
-
</div>
|
|
24
|
-
|
|
25
|
-
<div
|
|
26
|
-
v-if="$slots.valeurDroite"
|
|
27
|
-
class="label-droite"
|
|
28
|
-
>
|
|
29
|
-
<slot name="valeurDroite" />
|
|
30
|
-
</div>
|
|
31
|
-
</div>
|
|
32
|
-
</component>
|
|
33
|
-
|
|
34
|
-
<div
|
|
35
|
-
v-if="afficherDetails"
|
|
36
|
-
class="details"
|
|
37
|
-
>
|
|
38
|
-
<slot name="details">
|
|
39
|
-
<span v-html="texteDetaille"></span>
|
|
40
|
-
</slot>
|
|
41
|
-
</div>
|
|
42
|
-
</v-col>
|
|
43
|
-
|
|
44
|
-
<!-- Switch -->
|
|
45
|
-
<v-col
|
|
46
|
-
cols="2"
|
|
47
|
-
xl="1"
|
|
48
|
-
class="d-flex align-center justify-end py-0"
|
|
49
|
-
>
|
|
50
|
-
<span class="d-inline-flex">
|
|
51
|
-
<v-switch
|
|
52
|
-
:id="switchId"
|
|
53
|
-
class="switch-compact switch-tristate"
|
|
54
|
-
:class="{
|
|
55
|
-
'is-null': maValeur === null,
|
|
56
|
-
'is-true': maValeur === true,
|
|
57
|
-
'is-false': maValeur === false,
|
|
58
|
-
}"
|
|
59
|
-
:disabled="desactiver"
|
|
60
|
-
hide-details
|
|
61
|
-
|
|
62
|
-
:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
@click.prevent="onToggle"
|
|
66
|
-
@keydown.enter.prevent="onToggle"
|
|
67
|
-
@keydown.space.prevent="onToggle"
|
|
68
|
-
/>
|
|
69
|
-
|
|
70
|
-
<v-tooltip
|
|
71
|
-
v-if="maValeur === null"
|
|
72
|
-
activator="parent"
|
|
73
|
-
>
|
|
74
|
-
{{ $t('csqc.csqcOptionSwitch.indeterminee') }}
|
|
75
|
-
</v-tooltip>
|
|
76
|
-
</span>
|
|
77
|
-
</v-col>
|
|
78
|
-
</v-row>
|
|
79
|
-
</template>
|
|
80
|
-
|
|
81
|
-
<script setup lang="ts">
|
|
82
|
-
import { computed, useSlots } from 'vue'
|
|
83
|
-
|
|
84
|
-
type TriBool = boolean | null
|
|
85
|
-
|
|
86
|
-
const props = defineProps({
|
|
87
|
-
valeurInverse: { type: Boolean, default: false },
|
|
88
|
-
desactiver: { type: Boolean, default: false },
|
|
89
|
-
modelValue: { type: [Boolean, null] as unknown as () => TriBool, default: false },
|
|
90
|
-
autoriserNull: { type: Boolean, default: false },
|
|
91
|
-
texte: { type: String, required: true },
|
|
92
|
-
texteDetaille: { type: String, default: '' },
|
|
93
|
-
labelCliquable: { type: Boolean, default: true },
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
const emit = defineEmits<{
|
|
97
|
-
(e: 'update:modelValue', v: TriBool): void
|
|
98
|
-
}>()
|
|
99
|
-
const isIndeterminate = computed(() => maValeur.value === null)
|
|
100
|
-
|
|
101
|
-
const slots = useSlots()
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Valeur "logique" du composant (après inversion).
|
|
105
|
-
* Peut être true/false/null si allowNull=true.
|
|
106
|
-
*/
|
|
107
|
-
const maValeur = computed<TriBool>({
|
|
108
|
-
get: () => (props.valeurInverse ? inverseTri(props.modelValue) : props.modelValue),
|
|
109
|
-
set: v => emit('update:modelValue', props.valeurInverse ? inverseTri(v) : v),
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
function inverseTri(v: TriBool): TriBool {
|
|
113
|
-
if (v === null) return null
|
|
114
|
-
return !v
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* v-switch attend un bool pour afficher ON/OFF.
|
|
119
|
-
* - null => on affiche OFF (false) visuellement
|
|
120
|
-
*/
|
|
121
|
-
const switchChecked = computed(() => maValeur.value === true)
|
|
122
|
-
|
|
123
|
-
function onToggle() {
|
|
124
|
-
if (props.desactiver) return
|
|
125
|
-
|
|
126
|
-
// Mode tri-state
|
|
127
|
-
if (props.autoriserNull) {
|
|
128
|
-
// cycle: null -> true -> false -> null
|
|
129
|
-
const cur = maValeur.value
|
|
130
|
-
const next: TriBool = cur === null ? true : cur === true ? false : null
|
|
131
|
-
maValeur.value = next
|
|
132
|
-
return
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Mode classique (ne change rien vs avant)
|
|
136
|
-
maValeur.value = !switchChecked.value
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const afficherDetails = computed(() => {
|
|
140
|
-
const slotExiste = !!slots.details?.().length
|
|
141
|
-
return slotExiste || props.texteDetaille.trim().length > 0
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
const switchId = `sw_${Math.random().toString(36).slice(2)}`
|
|
145
|
-
</script>
|
|
146
|
-
|
|
147
|
-
<style scoped>
|
|
148
|
-
/* OFF (false) */
|
|
149
|
-
:deep(.switch-tristate.is-false .v-switch__track) {
|
|
150
|
-
opacity: 0.22 !important;
|
|
151
|
-
}
|
|
152
|
-
:deep(.switch-tristate.is-false .v-switch__thumb) {
|
|
153
|
-
opacity: 0.65 !important;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/* NULL (centre + très distinct) */
|
|
157
|
-
:deep(.switch-tristate.is-null .v-switch__track) {
|
|
158
|
-
background:
|
|
159
|
-
repeating-linear-gradient(
|
|
160
|
-
45deg,
|
|
161
|
-
rgba(255, 255, 255, 0.22),
|
|
162
|
-
rgba(255, 255, 255, 0.22) 6px,
|
|
163
|
-
rgba(255, 255, 255, 0.05) 6px,
|
|
164
|
-
rgba(255, 255, 255, 0.05) 12px
|
|
165
|
-
),
|
|
166
|
-
rgb(var(--v-theme-warning)) !important;
|
|
167
|
-
opacity: 1 !important;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
:deep(.switch-tristate.is-null .v-switch__thumb) {
|
|
171
|
-
background: white !important;
|
|
172
|
-
box-shadow:
|
|
173
|
-
0 0 0 2px rgba(var(--v-theme-warning), 0.55),
|
|
174
|
-
0 2px 6px rgba(0, 0, 0, 0.18) !important;
|
|
175
|
-
opacity: 1 !important;
|
|
176
|
-
transform: translateX(calc((100% - 20px) / 2)) !important;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/* Icône minus visible sur fond blanc */
|
|
180
|
-
:deep(.switch-tristate.is-null .v-selection-control__input-icon) {
|
|
181
|
-
color: rgb(var(--v-theme-warning)) !important;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/* Transition */
|
|
185
|
-
:deep(.switch-tristate .v-switch__thumb) {
|
|
186
|
-
transition:
|
|
187
|
-
transform 0.18s ease,
|
|
188
|
-
background 0.18s ease,
|
|
189
|
-
box-shadow 0.18s ease;
|
|
190
|
-
}
|
|
191
|
-
.labelSwitchSiSwitchApres {
|
|
192
|
-
font-weight: 700;
|
|
193
|
-
line-height: 1.2;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
.label-ligne {
|
|
197
|
-
align-items: baseline;
|
|
198
|
-
display: flex;
|
|
199
|
-
gap: 12px;
|
|
200
|
-
justify-content: space-between;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
.label-gauche {
|
|
204
|
-
min-width: 0;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
.label-droite {
|
|
208
|
-
font-weight: 400; /* pas en gras */
|
|
209
|
-
opacity: 0.75;
|
|
210
|
-
white-space: nowrap; /* reste sur la même ligne */
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
.label-cliquable {
|
|
214
|
-
cursor: pointer;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
.details {
|
|
218
|
-
margin-top: 4px;
|
|
219
|
-
}
|
|
220
|
-
</style>
|
|
1
|
+
<template>
|
|
2
|
+
<v-row
|
|
3
|
+
density="compact"
|
|
4
|
+
class="align-center"
|
|
5
|
+
>
|
|
6
|
+
<!-- Texte + détails -->
|
|
7
|
+
<v-col
|
|
8
|
+
cols="10"
|
|
9
|
+
xl="11"
|
|
10
|
+
class="py-0"
|
|
11
|
+
>
|
|
12
|
+
<component
|
|
13
|
+
:is="labelCliquable ? 'label' : 'div'"
|
|
14
|
+
:for="labelCliquable ? switchId : undefined"
|
|
15
|
+
class="labelSwitchSiSwitchApres"
|
|
16
|
+
:class="{ 'label-cliquable': labelCliquable && !desactiver }"
|
|
17
|
+
>
|
|
18
|
+
<div class="label-ligne">
|
|
19
|
+
<div class="label-gauche">
|
|
20
|
+
<slot name="label">
|
|
21
|
+
{{ texte }}
|
|
22
|
+
</slot>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div
|
|
26
|
+
v-if="$slots.valeurDroite"
|
|
27
|
+
class="label-droite"
|
|
28
|
+
>
|
|
29
|
+
<slot name="valeurDroite" />
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</component>
|
|
33
|
+
|
|
34
|
+
<div
|
|
35
|
+
v-if="afficherDetails"
|
|
36
|
+
class="details"
|
|
37
|
+
>
|
|
38
|
+
<slot name="details">
|
|
39
|
+
<span v-html="texteDetaille"></span>
|
|
40
|
+
</slot>
|
|
41
|
+
</div>
|
|
42
|
+
</v-col>
|
|
43
|
+
|
|
44
|
+
<!-- Switch -->
|
|
45
|
+
<v-col
|
|
46
|
+
cols="2"
|
|
47
|
+
xl="1"
|
|
48
|
+
class="d-flex align-center justify-end py-0"
|
|
49
|
+
>
|
|
50
|
+
<span class="d-inline-flex">
|
|
51
|
+
<v-switch
|
|
52
|
+
:id="switchId"
|
|
53
|
+
class="switch-compact switch-tristate"
|
|
54
|
+
:class="{
|
|
55
|
+
'is-null': maValeur === null,
|
|
56
|
+
'is-true': maValeur === true,
|
|
57
|
+
'is-false': maValeur === false,
|
|
58
|
+
}"
|
|
59
|
+
:disabled="desactiver"
|
|
60
|
+
hide-details
|
|
61
|
+
:model-value="switchChecked"
|
|
62
|
+
:indeterminate="isIndeterminate"
|
|
63
|
+
indeterminate-icon="mdi-minus"
|
|
64
|
+
v-bind="$attrs"
|
|
65
|
+
@click.prevent="onToggle"
|
|
66
|
+
@keydown.enter.prevent="onToggle"
|
|
67
|
+
@keydown.space.prevent="onToggle"
|
|
68
|
+
/>
|
|
69
|
+
|
|
70
|
+
<v-tooltip
|
|
71
|
+
v-if="maValeur === null"
|
|
72
|
+
activator="parent"
|
|
73
|
+
>
|
|
74
|
+
{{ $t('csqc.csqcOptionSwitch.indeterminee') }}
|
|
75
|
+
</v-tooltip>
|
|
76
|
+
</span>
|
|
77
|
+
</v-col>
|
|
78
|
+
</v-row>
|
|
79
|
+
</template>
|
|
80
|
+
|
|
81
|
+
<script setup lang="ts">
|
|
82
|
+
import { computed, useSlots } from 'vue'
|
|
83
|
+
|
|
84
|
+
type TriBool = boolean | null
|
|
85
|
+
|
|
86
|
+
const props = defineProps({
|
|
87
|
+
valeurInverse: { type: Boolean, default: false },
|
|
88
|
+
desactiver: { type: Boolean, default: false },
|
|
89
|
+
modelValue: { type: [Boolean, null] as unknown as () => TriBool, default: false },
|
|
90
|
+
autoriserNull: { type: Boolean, default: false },
|
|
91
|
+
texte: { type: String, required: true },
|
|
92
|
+
texteDetaille: { type: String, default: '' },
|
|
93
|
+
labelCliquable: { type: Boolean, default: true },
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
const emit = defineEmits<{
|
|
97
|
+
(e: 'update:modelValue', v: TriBool): void
|
|
98
|
+
}>()
|
|
99
|
+
const isIndeterminate = computed(() => maValeur.value === null)
|
|
100
|
+
|
|
101
|
+
const slots = useSlots()
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Valeur "logique" du composant (après inversion).
|
|
105
|
+
* Peut être true/false/null si allowNull=true.
|
|
106
|
+
*/
|
|
107
|
+
const maValeur = computed<TriBool>({
|
|
108
|
+
get: () => (props.valeurInverse ? inverseTri(props.modelValue) : props.modelValue),
|
|
109
|
+
set: v => emit('update:modelValue', props.valeurInverse ? inverseTri(v) : v),
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
function inverseTri(v: TriBool): TriBool {
|
|
113
|
+
if (v === null) return null
|
|
114
|
+
return !v
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* v-switch attend un bool pour afficher ON/OFF.
|
|
119
|
+
* - null => on affiche OFF (false) visuellement
|
|
120
|
+
*/
|
|
121
|
+
const switchChecked = computed(() => maValeur.value === true)
|
|
122
|
+
|
|
123
|
+
function onToggle() {
|
|
124
|
+
if (props.desactiver) return
|
|
125
|
+
|
|
126
|
+
// Mode tri-state
|
|
127
|
+
if (props.autoriserNull) {
|
|
128
|
+
// cycle: null -> true -> false -> null
|
|
129
|
+
const cur = maValeur.value
|
|
130
|
+
const next: TriBool = cur === null ? true : cur === true ? false : null
|
|
131
|
+
maValeur.value = next
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Mode classique (ne change rien vs avant)
|
|
136
|
+
maValeur.value = !switchChecked.value
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const afficherDetails = computed(() => {
|
|
140
|
+
const slotExiste = !!slots.details?.().length
|
|
141
|
+
return slotExiste || props.texteDetaille.trim().length > 0
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
const switchId = `sw_${Math.random().toString(36).slice(2)}`
|
|
145
|
+
</script>
|
|
146
|
+
|
|
147
|
+
<style scoped>
|
|
148
|
+
/* OFF (false) */
|
|
149
|
+
:deep(.switch-tristate.is-false .v-switch__track) {
|
|
150
|
+
opacity: 0.22 !important;
|
|
151
|
+
}
|
|
152
|
+
:deep(.switch-tristate.is-false .v-switch__thumb) {
|
|
153
|
+
opacity: 0.65 !important;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* NULL (centre + très distinct) */
|
|
157
|
+
:deep(.switch-tristate.is-null .v-switch__track) {
|
|
158
|
+
background:
|
|
159
|
+
repeating-linear-gradient(
|
|
160
|
+
45deg,
|
|
161
|
+
rgba(255, 255, 255, 0.22),
|
|
162
|
+
rgba(255, 255, 255, 0.22) 6px,
|
|
163
|
+
rgba(255, 255, 255, 0.05) 6px,
|
|
164
|
+
rgba(255, 255, 255, 0.05) 12px
|
|
165
|
+
),
|
|
166
|
+
rgb(var(--v-theme-warning)) !important;
|
|
167
|
+
opacity: 1 !important;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
:deep(.switch-tristate.is-null .v-switch__thumb) {
|
|
171
|
+
background: white !important;
|
|
172
|
+
box-shadow:
|
|
173
|
+
0 0 0 2px rgba(var(--v-theme-warning), 0.55),
|
|
174
|
+
0 2px 6px rgba(0, 0, 0, 0.18) !important;
|
|
175
|
+
opacity: 1 !important;
|
|
176
|
+
transform: translateX(calc((100% - 20px) / 2)) !important;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* Icône minus visible sur fond blanc */
|
|
180
|
+
:deep(.switch-tristate.is-null .v-selection-control__input-icon) {
|
|
181
|
+
color: rgb(var(--v-theme-warning)) !important;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* Transition */
|
|
185
|
+
:deep(.switch-tristate .v-switch__thumb) {
|
|
186
|
+
transition:
|
|
187
|
+
transform 0.18s ease,
|
|
188
|
+
background 0.18s ease,
|
|
189
|
+
box-shadow 0.18s ease;
|
|
190
|
+
}
|
|
191
|
+
.labelSwitchSiSwitchApres {
|
|
192
|
+
font-weight: 700;
|
|
193
|
+
line-height: 1.2;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.label-ligne {
|
|
197
|
+
align-items: baseline;
|
|
198
|
+
display: flex;
|
|
199
|
+
gap: 12px;
|
|
200
|
+
justify-content: space-between;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.label-gauche {
|
|
204
|
+
min-width: 0;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.label-droite {
|
|
208
|
+
font-weight: 400; /* pas en gras */
|
|
209
|
+
opacity: 0.75;
|
|
210
|
+
white-space: nowrap; /* reste sur la même ligne */
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.label-cliquable {
|
|
214
|
+
cursor: pointer;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.details {
|
|
218
|
+
margin-top: 4px;
|
|
219
|
+
}
|
|
220
|
+
</style>
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
<v-col>
|
|
7
7
|
<slot name="recherche"></slot>
|
|
8
8
|
<Recherche
|
|
9
|
-
v-bind="$attrs"
|
|
10
9
|
:afficher="rechercheAfficher"
|
|
11
10
|
:recherche-texte="rechercheTexte"
|
|
12
11
|
:chargement="chargementListe"
|
|
@@ -16,6 +15,7 @@
|
|
|
16
15
|
:recherche-avancee-largeur="rechercheAvanceeLargeur"
|
|
17
16
|
:recherche-avancee-style="rechercheAvanceeStyle"
|
|
18
17
|
class="flex-grow-1"
|
|
18
|
+
v-bind="$attrs"
|
|
19
19
|
@update:recherche="chargerRecherche"
|
|
20
20
|
@panneau:etat="onPanelChange"
|
|
21
21
|
>
|
|
@@ -304,7 +304,7 @@
|
|
|
304
304
|
// Style en-tête tableau (design Québec)
|
|
305
305
|
styleEntete: {
|
|
306
306
|
type: String as PropType<'standard' | 'standardGris' | 'simplifie'>,
|
|
307
|
-
default: '
|
|
307
|
+
default: 'simplifie',
|
|
308
308
|
},
|
|
309
309
|
|
|
310
310
|
// Modale suppression
|