codevdesign 1.0.39 → 1.0.40

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,205 +1,205 @@
1
- <template>
2
- <v-toolbar
3
- color="primary"
4
- height="72px"
5
- elevation="1"
6
- >
7
- <v-row
8
- class="pl-6 pr-6"
9
- align="center"
10
- no-gutters
11
- >
12
- <!-- Première colonne : Logo -->
13
- <v-col cols="auto">
14
- <a
15
- :href="href"
16
- :target="cssUrlValide ? '_blank' : undefined"
17
- :rel="cssUrlValide ? 'noopener noreferrer' : undefined"
18
- >
19
- <!-- Placeholder (même taille) pendant la décision -->
20
- <div
21
- v-if="!ready"
22
- class="logo-placeholder"
23
- ></div>
24
- <!-- On ne rend l'image qu'une fois la source choisie -->
25
- <img
26
- v-else
27
- class="logo-img"
28
- id="pivImage"
29
- :src="currentSrc!"
30
- :alt="$t('csqc.pivFooter.logoAlt')"
31
- decoding="async"
32
- loading="eager"
33
- @error="ErreurLogo"
34
- />
35
- </a>
36
- </v-col>
37
-
38
- <!-- Colonne pour le nom de l'application (Pour le mode desktop) -->
39
- <v-col
40
- v-if="!estMobile"
41
- class="d-flex justify-center"
42
- >
43
- <v-app-bar-title
44
- class="pl-12 ml-12"
45
- style="font-size: 16px !important"
46
- >
47
- {{ formulaireNom }}
48
- </v-app-bar-title>
49
- </v-col>
50
-
51
- <!-- Colonne pour le bouton de langue et icône d'aide -->
52
- <v-col class="d-none d-flex justify-end">
53
- <!-- langue -->
54
- <v-btn
55
- variant="text"
56
- @click="enregistrerLangue()"
57
- >{{ $t('csqc.pivEntete.langue') }}
58
- </v-btn>
59
-
60
- <!-- icône d'aide si dispo -->
61
- <v-btn
62
- v-if="aideUrl"
63
- icon="mdi-help-circle-outline"
64
- :href="aideUrl"
65
- target="_blank"
66
- rel="noopener noreferrer"
67
- style="margin-top: -6px"
68
- >
69
- </v-btn>
70
-
71
- <slot name="droite"></slot>
72
- </v-col>
73
-
74
- <!-- Colonne pour le nom de l'application (Pour le mode mobile) -->
75
- <v-col
76
- v-if="props.estMobile"
77
- cols="12"
78
- >
79
- <v-app-bar-title style="font-size: 16px !important">
80
- {{ formulaireNom }}
81
- </v-app-bar-title>
82
- </v-col>
83
- </v-row>
84
- </v-toolbar>
85
- </template>
86
-
87
- <script setup lang="ts">
88
- import { ref, watch, computed } from 'vue'
89
- import { useLocale } from 'vuetify'
90
- import { useI18n } from 'vue-i18n'
91
-
92
- const { current } = useLocale()
93
- const props = defineProps({
94
- estMobile: { type: Boolean, default: false },
95
- urlBase: { type: String, required: true },
96
- aideUrl: { type: String, default: '' },
97
- cssUrl: { type: String, default: '' },
98
- logoUrl: { type: String, default: '' },
99
- lienLogo: { type: String, default: '' },
100
- formulaireId: { type: Number, default: 0 },
101
- formulaireNom: { type: String, default: '' },
102
- })
103
- const emit = defineEmits(['changementLangue'])
104
- const { t } = useI18n()
105
-
106
- const FALLBACK = '/portail/images/QUEBEC_blanc.svg'
107
- const currentSrc = ref<string | null>(null) // pas d'image tant que null
108
- const ready = ref(false)
109
-
110
- const formulaireNom = computed(() => {
111
- return props.formulaireNom || t('nom_application')
112
- })
113
-
114
- const href = computed(() => (props.cssUrl?.trim() ? props.cssUrl.trim() : '/'))
115
- const cssUrlValide = computed(() => /^https?:\/\//i.test(href.value))
116
-
117
- let loadToken = 0 // ← identifiant de “requête” pour annuler logiquement
118
-
119
- // Sur changement de l’URL du logo, on tente de le charger, sinon fallback apres 3.5 secs
120
- watch(
121
- () => props.logoUrl,
122
- async (nouvelle, ancienne) => {
123
- const url = (nouvelle ?? '').trim()
124
-
125
- // même URL → ne rien faire
126
- if (url === (ancienne ?? '').trim() && currentSrc.value !== null) return
127
-
128
- // pas d’URL → fallback immédiat
129
- if (!url) {
130
- currentSrc.value = FALLBACK
131
- ready.value = true
132
- return
133
- }
134
-
135
- // nouvelle tentative (avec “annulation logique”)
136
- const token = ++loadToken
137
- ready.value = false
138
- const ok = await loadWithTimeout(url, 3500)
139
- if (token !== loadToken) return // une nouvelle tentative a démarré entre-temps
140
-
141
- currentSrc.value = ok ? url : FALLBACK
142
- ready.value = true
143
- },
144
- { immediate: true },
145
- )
146
-
147
- // Charge une image avec un timeout
148
- function loadWithTimeout(url: string, timeoutMs: number): Promise<boolean> {
149
- return new Promise<boolean>(resolve => {
150
- const img = new Image()
151
- const timer = setTimeout(() => {
152
- // trop long → on abandonne
153
- img.src = '' // stoppe le chargement
154
- resolve(false)
155
- }, timeoutMs)
156
-
157
- img.onload = () => {
158
- clearTimeout(timer)
159
- resolve(true)
160
- }
161
- img.onerror = () => {
162
- clearTimeout(timer)
163
- resolve(false)
164
- }
165
- img.src = url
166
- })
167
- }
168
-
169
- // Si l'image choisie a un problème, switch sur le fallback
170
- function ErreurLogo() {
171
- if (currentSrc.value !== FALLBACK) currentSrc.value = FALLBACK
172
- }
173
-
174
- const enregistrerLangue = (): void => {
175
- const langueDispo: string = current.value === 'fr' ? 'en' : 'fr'
176
- let returnUrl = window.location.pathname + window.location.search
177
- if (import.meta.env.MODE === 'development') {
178
- returnUrl = '/'
179
- }
180
- window.location.href =
181
- props.urlBase + `/Traducteur/SetLanguage?culture=${langueDispo}&returnUrl=${encodeURIComponent(returnUrl)}`
182
- emit('changementLangue')
183
- }
184
- </script>
185
-
186
- <style scoped>
187
- .container {
188
- max-width: none !important;
189
- }
190
- .theme--light.v-app-bar.v-toolbar.v-sheet {
191
- background: #095797;
192
-
193
- color: #fff;
194
- }
195
- .logo-placeholder {
196
- height: 72px;
197
- width: 180px;
198
- }
199
- .logo-img {
200
- height: 72px;
201
- transition: opacity 0.15s;
202
- opacity: 1;
203
- display: block;
204
- }
205
- </style>
1
+ <template>
2
+ <v-toolbar
3
+ color="primary"
4
+ height="72px"
5
+ elevation="1"
6
+ >
7
+ <v-row
8
+ class="pl-6 pr-6"
9
+ align="center"
10
+ no-gutters
11
+ >
12
+ <!-- Première colonne : Logo -->
13
+ <v-col cols="auto">
14
+ <a
15
+ :href="href"
16
+ :target="cssUrlValide ? '_blank' : undefined"
17
+ :rel="cssUrlValide ? 'noopener noreferrer' : undefined"
18
+ >
19
+ <!-- Placeholder (même taille) pendant la décision -->
20
+ <div
21
+ v-if="!ready"
22
+ class="logo-placeholder"
23
+ ></div>
24
+ <!-- On ne rend l'image qu'une fois la source choisie -->
25
+ <img
26
+ v-else
27
+ class="logo-img"
28
+ id="pivImage"
29
+ :src="currentSrc!"
30
+ :alt="$t('csqc.pivFooter.logoAlt')"
31
+ decoding="async"
32
+ loading="eager"
33
+ @error="ErreurLogo"
34
+ />
35
+ </a>
36
+ </v-col>
37
+
38
+ <!-- Colonne pour le nom de l'application (Pour le mode desktop) -->
39
+ <v-col
40
+ v-if="!estMobile"
41
+ class="d-flex justify-center"
42
+ >
43
+ <v-app-bar-title
44
+ class="pl-12 ml-12"
45
+ style="font-size: 16px !important"
46
+ >
47
+ {{ formulaireNom }}
48
+ </v-app-bar-title>
49
+ </v-col>
50
+
51
+ <!-- Colonne pour le bouton de langue et icône d'aide -->
52
+ <v-col class="d-none d-flex justify-end">
53
+ <!-- langue -->
54
+ <v-btn
55
+ variant="text"
56
+ @click="enregistrerLangue()"
57
+ >{{ $t('csqc.pivEntete.langue') }}
58
+ </v-btn>
59
+
60
+ <!-- icône d'aide si dispo -->
61
+ <v-btn
62
+ v-if="aideUrl"
63
+ icon="mdi-help-circle-outline"
64
+ :href="aideUrl"
65
+ target="_blank"
66
+ rel="noopener noreferrer"
67
+ style="margin-top: -6px"
68
+ >
69
+ </v-btn>
70
+
71
+ <slot name="droite"></slot>
72
+ </v-col>
73
+
74
+ <!-- Colonne pour le nom de l'application (Pour le mode mobile) -->
75
+ <v-col
76
+ v-if="props.estMobile"
77
+ cols="12"
78
+ >
79
+ <v-app-bar-title style="font-size: 16px !important">
80
+ {{ formulaireNom }}
81
+ </v-app-bar-title>
82
+ </v-col>
83
+ </v-row>
84
+ </v-toolbar>
85
+ </template>
86
+
87
+ <script setup lang="ts">
88
+ import { ref, watch, computed } from 'vue'
89
+ import { useLocale } from 'vuetify'
90
+ import { useI18n } from 'vue-i18n'
91
+
92
+ const { current } = useLocale()
93
+ const props = defineProps({
94
+ estMobile: { type: Boolean, default: false },
95
+ urlBase: { type: String, required: true },
96
+ aideUrl: { type: String, default: '' },
97
+ cssUrl: { type: String, default: '' },
98
+ logoUrl: { type: String, default: '' },
99
+ lienLogo: { type: String, default: '' },
100
+ formulaireId: { type: Number, default: 0 },
101
+ formulaireNom: { type: String, default: '' },
102
+ })
103
+ const emit = defineEmits(['changementLangue'])
104
+ const { t } = useI18n()
105
+
106
+ const FALLBACK = '/portail/images/QUEBEC_blanc.svg'
107
+ const currentSrc = ref<string | null>(null) // pas d'image tant que null
108
+ const ready = ref(false)
109
+
110
+ const formulaireNom = computed(() => {
111
+ return props.formulaireNom || t('nom_application')
112
+ })
113
+
114
+ const href = computed(() => (props.cssUrl?.trim() ? props.cssUrl.trim() : '/'))
115
+ const cssUrlValide = computed(() => /^https?:\/\//i.test(href.value))
116
+
117
+ let loadToken = 0 // ← identifiant de “requête” pour annuler logiquement
118
+
119
+ // Sur changement de l’URL du logo, on tente de le charger, sinon fallback apres 3.5 secs
120
+ watch(
121
+ () => props.logoUrl,
122
+ async (nouvelle, ancienne) => {
123
+ const url = (nouvelle ?? '').trim()
124
+
125
+ // même URL → ne rien faire
126
+ if (url === (ancienne ?? '').trim() && currentSrc.value !== null) return
127
+
128
+ // pas d’URL → fallback immédiat
129
+ if (!url) {
130
+ currentSrc.value = FALLBACK
131
+ ready.value = true
132
+ return
133
+ }
134
+
135
+ // nouvelle tentative (avec “annulation logique”)
136
+ const token = ++loadToken
137
+ ready.value = false
138
+ const ok = await loadWithTimeout(url, 3500)
139
+ if (token !== loadToken) return // une nouvelle tentative a démarré entre-temps
140
+
141
+ currentSrc.value = ok ? url : FALLBACK
142
+ ready.value = true
143
+ },
144
+ { immediate: true },
145
+ )
146
+
147
+ // Charge une image avec un timeout
148
+ function loadWithTimeout(url: string, timeoutMs: number): Promise<boolean> {
149
+ return new Promise<boolean>(resolve => {
150
+ const img = new Image()
151
+ const timer = setTimeout(() => {
152
+ // trop long → on abandonne
153
+ img.src = '' // stoppe le chargement
154
+ resolve(false)
155
+ }, timeoutMs)
156
+
157
+ img.onload = () => {
158
+ clearTimeout(timer)
159
+ resolve(true)
160
+ }
161
+ img.onerror = () => {
162
+ clearTimeout(timer)
163
+ resolve(false)
164
+ }
165
+ img.src = url
166
+ })
167
+ }
168
+
169
+ // Si l'image choisie a un problème, switch sur le fallback
170
+ function ErreurLogo() {
171
+ if (currentSrc.value !== FALLBACK) currentSrc.value = FALLBACK
172
+ }
173
+
174
+ const enregistrerLangue = (): void => {
175
+ const langueDispo: string = current.value === 'fr' ? 'en' : 'fr'
176
+ let returnUrl = window.location.pathname + window.location.search
177
+ if (import.meta.env.MODE === 'development') {
178
+ returnUrl = '/'
179
+ }
180
+ window.location.href =
181
+ props.urlBase + `/Traducteur/SetLanguage?culture=${langueDispo}&returnUrl=${encodeURIComponent(returnUrl)}`
182
+ emit('changementLangue')
183
+ }
184
+ </script>
185
+
186
+ <style scoped>
187
+ .container {
188
+ max-width: none !important;
189
+ }
190
+ .theme--light.v-app-bar.v-toolbar.v-sheet {
191
+ background: #095797;
192
+
193
+ color: #fff;
194
+ }
195
+ .logo-placeholder {
196
+ height: 72px;
197
+ width: 180px;
198
+ }
199
+ .logo-img {
200
+ height: 72px;
201
+ transition: opacity 0.15s;
202
+ opacity: 1;
203
+ display: block;
204
+ }
205
+ </style>
package/index.ts CHANGED
@@ -1,75 +1,75 @@
1
- import csqcAlerteErreur from './composants/csqcAlerteErreur.vue'
2
- import csqcDialogue from './composants/csqcDialogue.vue'
3
- import csqcOptionSwitch from './composants/csqcOptionSwitch.vue'
4
- import csqcRecherche from './composants/csqcRecherche.vue'
5
- import csqcSnackbar from './composants/csqcSnackbar.vue'
6
- import csqcTiroir from './composants/csqcTiroir.vue'
7
- import pivEntete from './composants/gabarit/pivEntete.vue'
8
- import pivFooter from './composants/gabarit/pivPiedPage.vue'
9
- import csqcMenu from './composants/gabarit/csqcMenu.vue'
10
- import csqcConfirmation from './composants/csqcConfirmation.vue'
11
- import csqcSaisie from './composants/csqcModaleSaisie.vue'
12
- import csqcDate from './composants/csqcDate.vue'
13
- import csqcTable from './composants/csqcTable/csqcTable.vue'
14
- import csqcCodeBudgetaire from './composants/csqcCodeBudgetaireGenerique.vue'
15
- import csqcChaise from './composants/csqcChaise/chaiseConteneur.vue'
16
- import csqcAide from './composants/csqcAide.vue'
17
- import csqcEntete from './composants/csqcEntete.vue'
18
- import csqcTexteBilingue from './composants/csqcTexteBilingue.vue'
19
-
20
- import csqcEditeurTexteRiche from './composants/csqcEditeurTexteRiche.vue'
21
- import csqcImportCSV from './composants/csqcImportCSV.vue'
22
- import csqcRechercheUtilisateur from './composants/csqcRechercheUtilisateur.vue'
23
- import validateurs from './composants/validateurs'
24
-
25
- // modèles
26
- import NotificationGabaritDefaut from './modeles/notificationGabaritDefaut'
27
- import modeleSnackbar from './modeles/composants/snackbar'
28
- import modeleDatatableColonne from './modeles/composants/datatableColonne'
29
- import apiReponse from './modeles/apiReponse'
30
- import data from './modeles/data'
31
- import response from './modeles/response'
32
-
33
- // outils
34
- import csqcRafraichisseurToken from './outils/rafraichisseurToken'
35
- import csqcOutils from './outils/csqcOutils'
36
-
37
- // i18n
38
- import csqcEn from './locales/en.json'
39
- import csqcFr from './locales/fr.json'
40
-
41
-
42
- export {
43
- csqcFr,
44
- csqcEn,
45
- csqcAlerteErreur,
46
- csqcDialogue,
47
- csqcConfirmation,
48
- csqcSaisie,
49
- csqcDate,
50
- csqcOptionSwitch,
51
- csqcRecherche,
52
- csqcSnackbar,
53
- csqcTable,
54
- csqcTiroir,
55
- csqcMenu,
56
- csqcCodeBudgetaire,
57
- csqcChaise,
58
- pivFooter,
59
- pivEntete,
60
- csqcAide,
61
- csqcEntete,
62
- csqcTexteBilingue,
63
- csqcEditeurTexteRiche,
64
- validateurs,
65
- csqcImportCSV,
66
- csqcRechercheUtilisateur,
67
- csqcRafraichisseurToken,
68
- csqcOutils,
69
- modeleSnackbar,
70
- modeleDatatableColonne as colonne,
71
- apiReponse,
72
- data,
73
- response,
74
- NotificationGabaritDefaut,
75
- }
1
+ import csqcAlerteErreur from './composants/csqcAlerteErreur.vue'
2
+ import csqcDialogue from './composants/csqcDialogue.vue'
3
+ import csqcOptionSwitch from './composants/csqcOptionSwitch.vue'
4
+ import csqcRecherche from './composants/csqcRecherche.vue'
5
+ import csqcSnackbar from './composants/csqcSnackbar.vue'
6
+ import csqcTiroir from './composants/csqcTiroir.vue'
7
+ import pivEntete from './composants/gabarit/pivEntete.vue'
8
+ import pivFooter from './composants/gabarit/pivPiedPage.vue'
9
+ import csqcMenu from './composants/gabarit/csqcMenu.vue'
10
+ import csqcConfirmation from './composants/csqcConfirmation.vue'
11
+ import csqcSaisie from './composants/csqcModaleSaisie.vue'
12
+ import csqcDate from './composants/csqcDate.vue'
13
+ import csqcTable from './composants/csqcTable/csqcTable.vue'
14
+ import csqcCodeBudgetaire from './composants/csqcCodeBudgetaireGenerique.vue'
15
+ import csqcChaise from './composants/csqcChaise/chaiseConteneur.vue'
16
+ import csqcAide from './composants/csqcAide.vue'
17
+ import csqcEntete from './composants/csqcEntete.vue'
18
+ import csqcTexteBilingue from './composants/csqcTexteBilingue.vue'
19
+
20
+ import csqcEditeurTexteRiche from './composants/csqcEditeurTexteRiche.vue'
21
+ import csqcImportCSV from './composants/csqcImportCSV.vue'
22
+ import csqcRechercheUtilisateur from './composants/csqcRechercheUtilisateur.vue'
23
+ import validateurs from './composants/validateurs'
24
+
25
+ // modèles
26
+ import NotificationGabaritDefaut from './modeles/notificationGabaritDefaut'
27
+ import modeleSnackbar from './modeles/composants/snackbar'
28
+ import modeleDatatableColonne from './modeles/composants/datatableColonne'
29
+ import apiReponse from './modeles/apiReponse'
30
+ import data from './modeles/data'
31
+ import response from './modeles/response'
32
+
33
+ // outils
34
+ import csqcRafraichisseurToken from './outils/rafraichisseurToken'
35
+ import csqcOutils from './outils/csqcOutils'
36
+
37
+ // i18n
38
+ import csqcEn from './locales/en.json'
39
+ import csqcFr from './locales/fr.json'
40
+
41
+
42
+ export {
43
+ csqcFr,
44
+ csqcEn,
45
+ csqcAlerteErreur,
46
+ csqcDialogue,
47
+ csqcConfirmation,
48
+ csqcSaisie,
49
+ csqcDate,
50
+ csqcOptionSwitch,
51
+ csqcRecherche,
52
+ csqcSnackbar,
53
+ csqcTable,
54
+ csqcTiroir,
55
+ csqcMenu,
56
+ csqcCodeBudgetaire,
57
+ csqcChaise,
58
+ pivFooter,
59
+ pivEntete,
60
+ csqcAide,
61
+ csqcEntete,
62
+ csqcTexteBilingue,
63
+ csqcEditeurTexteRiche,
64
+ validateurs,
65
+ csqcImportCSV,
66
+ csqcRechercheUtilisateur,
67
+ csqcRafraichisseurToken,
68
+ csqcOutils,
69
+ modeleSnackbar,
70
+ modeleDatatableColonne as colonne,
71
+ apiReponse,
72
+ data,
73
+ response,
74
+ NotificationGabaritDefaut,
75
+ }
@@ -1,18 +1,18 @@
1
- export interface MenuItem {
2
- nom: string
3
- path: string
4
- droit: string
5
- }
6
-
7
- export interface SousListe {
8
- droit: string
9
- id: number
10
- nom: string
11
- }
12
-
13
- export interface SousListeItems {
14
- parentId: number
15
- nom: string
16
- path: string
17
- droit: string
18
- }
1
+ export interface MenuItem {
2
+ nom: string
3
+ path: string
4
+ droit: string
5
+ }
6
+
7
+ export interface SousListe {
8
+ droit: string
9
+ id: number
10
+ nom: string
11
+ }
12
+
13
+ export interface SousListeItems {
14
+ parentId: number
15
+ nom: string
16
+ path: string
17
+ droit: string
18
+ }
@@ -1,31 +1,31 @@
1
- import type { DataTableCompareFunction, DataTableHeader, FilterFunction } from 'vuetify'
2
- import { HeaderCellProps } from 'vuetify/lib/components/VDataTable/types.mjs';
3
- import { SelectItemKey } from 'vuetify/lib/util/helpers.mjs';
4
-
5
- class datatableColonne<T = any> implements DataTableHeader<T> {
6
- key?: 'data-table-group' | 'data-table-select' | 'data-table-expand' | (string & {});
7
- value?: SelectItemKey<T>;
8
- title?: string;
9
- fixed?: boolean | 'start' | 'end';
10
- align?: 'start' | 'end' | 'center';
11
- width?: number | string;
12
- minWidth?: number | string;
13
- maxWidth?: number | string;
14
- nowrap?: boolean;
15
- intent?: number;
16
- headerProps?: Record<string, any>;
17
- cellProps?: HeaderCellProps;
18
- sortable?: boolean;
19
- sort?: DataTableCompareFunction;
20
- sortRaw?: DataTableCompareFunction;
21
- filter?: FilterFunction;
22
- children?: DataTableHeader<T>[];
23
-
24
- constructor(title: string, key: string) {
25
- this.title = title
26
- this.key = key
27
- this.align = key === 'action' ? 'end' : key === 'estActif' ? 'center' : 'start'
28
- this.sortable = key !== 'action'
29
- }
30
- }
31
- export default datatableColonne
1
+ import type { DataTableCompareFunction, DataTableHeader, FilterFunction } from 'vuetify'
2
+ import { HeaderCellProps } from 'vuetify/lib/components/VDataTable/types.mjs';
3
+ import { SelectItemKey } from 'vuetify/lib/util/helpers.mjs';
4
+
5
+ class datatableColonne<T = any> implements DataTableHeader<T> {
6
+ key?: 'data-table-group' | 'data-table-select' | 'data-table-expand' | (string & {});
7
+ value?: SelectItemKey<T>;
8
+ title?: string;
9
+ fixed?: boolean | 'start' | 'end';
10
+ align?: 'start' | 'end' | 'center';
11
+ width?: number | string;
12
+ minWidth?: number | string;
13
+ maxWidth?: number | string;
14
+ nowrap?: boolean;
15
+ intent?: number;
16
+ headerProps?: Record<string, any>;
17
+ cellProps?: HeaderCellProps;
18
+ sortable?: boolean;
19
+ sort?: DataTableCompareFunction;
20
+ sortRaw?: DataTableCompareFunction;
21
+ filter?: FilterFunction;
22
+ children?: DataTableHeader<T>[];
23
+
24
+ constructor(title: string, key: string) {
25
+ this.title = title
26
+ this.key = key
27
+ this.align = key === 'action' ? 'end' : key === 'estActif' ? 'center' : 'start'
28
+ this.sortable = key !== 'action'
29
+ }
30
+ }
31
+ export default datatableColonne