codevdesign 0.0.67 → 0.0.69
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/csqcEditeurTexteRiche.vue +326 -0
- package/composants/csqcEntete.vue +17 -0
- package/composants/csqcTiroir.vue +155 -149
- package/index.ts +71 -70
- package/package.json +3 -3
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="editor">
|
|
3
|
+
<!-- Remount contrôlé pour TinyMCE -->
|
|
4
|
+
<Editor
|
|
5
|
+
v-if="editorReady"
|
|
6
|
+
v-model="editorValue"
|
|
7
|
+
:init="initOptions"
|
|
8
|
+
:disabled="desactiver"
|
|
9
|
+
@blur="surEvenementBlur"
|
|
10
|
+
@keydown="surEvenementBlur"
|
|
11
|
+
@input="surEvenementBlur"
|
|
12
|
+
@change="surEvenementBlur"
|
|
13
|
+
license-key="gpl"
|
|
14
|
+
/>
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script>
|
|
19
|
+
/* Vue 3 - Options API */
|
|
20
|
+
import Editor from '@tinymce/tinymce-vue'
|
|
21
|
+
import * as tinymce from 'tinymce/tinymce.js'
|
|
22
|
+
import 'tinymce/icons/default/icons.min.js'
|
|
23
|
+
import 'tinymce/themes/silver/theme.min.js'
|
|
24
|
+
import 'tinymce/models/dom/model.min.js'
|
|
25
|
+
import 'tinymce/skins/ui/oxide/skin.js'
|
|
26
|
+
import 'tinymce/skins/ui/oxide/content.js'
|
|
27
|
+
import 'tinymce/skins/content/default/content.js'
|
|
28
|
+
import 'tinymce-i18n/langs7/fr_FR'
|
|
29
|
+
|
|
30
|
+
/* Plugins */
|
|
31
|
+
import 'tinymce/plugins/autoresize/plugin.min.js'
|
|
32
|
+
import 'tinymce/plugins/advlist/plugin.min.js'
|
|
33
|
+
import 'tinymce/plugins/lists/plugin.min.js'
|
|
34
|
+
import 'tinymce/plugins/link/plugin.min.js'
|
|
35
|
+
import 'tinymce/plugins/autolink/plugin.min.js'
|
|
36
|
+
import 'tinymce/plugins/fullscreen/plugin.min.js'
|
|
37
|
+
import 'tinymce/plugins/table/plugin.min.js'
|
|
38
|
+
import 'tinymce/plugins/image/plugin.min.js' // activé/désactivé via "pluginsEffective"
|
|
39
|
+
import 'tinymce/plugins/emoticons/plugin.min.js'
|
|
40
|
+
import 'tinymce/plugins/emoticons/js/emojis.min.js'
|
|
41
|
+
import 'tinymce/plugins/code/plugin.min.js'
|
|
42
|
+
|
|
43
|
+
const openLink = (url, target) => {
|
|
44
|
+
if (!url) return
|
|
45
|
+
if (import.meta.env.MODE !== 'development') {
|
|
46
|
+
if (target && target !== '_blank') {
|
|
47
|
+
document.location.href = url
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
window.open(url, target || '_blank', 'noopener,noreferrer')
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default {
|
|
55
|
+
name: 'EditeurForm',
|
|
56
|
+
components: { Editor },
|
|
57
|
+
|
|
58
|
+
props: {
|
|
59
|
+
langue: { type: String, default: 'fr_FR' },
|
|
60
|
+
value: { type: String, default: '' },
|
|
61
|
+
interdireRedimension: { type: Boolean, default: false },
|
|
62
|
+
desactiver: { type: Boolean, default: false },
|
|
63
|
+
plugins: {
|
|
64
|
+
type: [String, Array],
|
|
65
|
+
default: 'autoresize table image link fullscreen advlist lists emoticons autolink code',
|
|
66
|
+
},
|
|
67
|
+
interdireImage: { type: Boolean, default: false },
|
|
68
|
+
imageTailleMaximale: { type: Number, default: 5 }, // en Mo
|
|
69
|
+
cacherBarreMenu: { type: Boolean, default: false },
|
|
70
|
+
cacherBarreOutils: { type: Boolean, default: false },
|
|
71
|
+
toolbar: {
|
|
72
|
+
type: [String, Array],
|
|
73
|
+
default:
|
|
74
|
+
'bold italic underline strikethrough | fontsizeselect | forecolor backcolor | ' +
|
|
75
|
+
'alignleft aligncenter alignright alignjustify | emoticons | bullist numlist | ' +
|
|
76
|
+
'image | link | outdent indent blockquote | undo redo | fullscreen | removeformat | table | code',
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
data() {
|
|
81
|
+
return {
|
|
82
|
+
editorReady: true,
|
|
83
|
+
_editor: null,
|
|
84
|
+
editorValue: this.value,
|
|
85
|
+
_reinitPending: false,
|
|
86
|
+
_reinitLock: false,
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
computed: {
|
|
91
|
+
// <= ICI on force un plafond d'image à 100 Mo (et on gère les valeurs non numériques)
|
|
92
|
+
imageTailleMaxMo() {
|
|
93
|
+
const n = Number(this.imageTailleMaximale)
|
|
94
|
+
if (!Number.isFinite(n)) return 5
|
|
95
|
+
return Math.min(100, Math.max(0, n))
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
// Nettoie la toolbar en retirant "image" et ses séparateurs orphelins
|
|
99
|
+
toolbarEffective() {
|
|
100
|
+
if (this.cacherBarreOutils) return false
|
|
101
|
+
const raw = Array.isArray(this.toolbar) ? this.toolbar.join(' | ') : this.toolbar
|
|
102
|
+
if (!this.interdireImage) return this._normalizeToolbar(raw)
|
|
103
|
+
|
|
104
|
+
const sansImage = raw
|
|
105
|
+
.replace(/\bimage\b/g, '') // retire le bouton
|
|
106
|
+
.replace(/\s*\|\s*/g, ' | ') // normalise les séparateurs
|
|
107
|
+
.replace(/(\s*\|\s*){2,}/g, ' | ') // évite les doubles |
|
|
108
|
+
.replace(/^\s*\|\s*|\s*\|\s*$/g, '') // retire | en début/fin
|
|
109
|
+
.replace(/\s{2,}/g, ' ') // espaces doublés
|
|
110
|
+
.trim()
|
|
111
|
+
|
|
112
|
+
return this._normalizeToolbar(sansImage)
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
// Plugins effectifs : retire "image" si interdireImage = true
|
|
116
|
+
pluginsEffective() {
|
|
117
|
+
const list = (Array.isArray(this.plugins) ? this.plugins : this.plugins.split(/\s+/))
|
|
118
|
+
.map(p => p.trim())
|
|
119
|
+
.filter(Boolean)
|
|
120
|
+
|
|
121
|
+
const filtered = this.interdireImage ? list.filter(p => p !== 'image') : list
|
|
122
|
+
return filtered.join(' ')
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
// Options TinyMCE réactives
|
|
126
|
+
initOptions() {
|
|
127
|
+
const opts = {
|
|
128
|
+
autolink: !this.desactiver,
|
|
129
|
+
autoresize: true,
|
|
130
|
+
branding: false,
|
|
131
|
+
browser_spellcheck: true,
|
|
132
|
+
content_css: 'default',
|
|
133
|
+
deprecation_warnings: false,
|
|
134
|
+
emoticons_database: 'emojis',
|
|
135
|
+
//height: this.height,
|
|
136
|
+
//min_heignt: 100,
|
|
137
|
+
//max_height: 800,
|
|
138
|
+
highlight_on_focus: false,
|
|
139
|
+
language: this.langue == 'en' ? 'en_US' : 'fr_FR',
|
|
140
|
+
license_key: 'gpl',
|
|
141
|
+
menubar: !this.cacherBarreMenu,
|
|
142
|
+
promotion: false,
|
|
143
|
+
resize: !this.interdireRedimension,
|
|
144
|
+
skin_url: 'default',
|
|
145
|
+
statusbar: this.cacherBarreOutils === false,
|
|
146
|
+
theme_advanced_resizing: true,
|
|
147
|
+
theme_advanced_resizing_use_cookie: false,
|
|
148
|
+
|
|
149
|
+
// Gestion images selon interdireImage
|
|
150
|
+
plugins: this.pluginsEffective,
|
|
151
|
+
toolbar: this.toolbarEffective,
|
|
152
|
+
automatic_uploads: !this.interdireImage,
|
|
153
|
+
object_resizing: !this.interdireImage,
|
|
154
|
+
paste_data_images: !this.interdireImage,
|
|
155
|
+
...(this.interdireImage ? {} : { file_picker_types: 'image' }),
|
|
156
|
+
|
|
157
|
+
// Certaines options images n'ont effet que si plugin "image" est actif
|
|
158
|
+
image_dimensions: !this.interdireImage,
|
|
159
|
+
resize_img_proportional: !this.interdireImage,
|
|
160
|
+
|
|
161
|
+
readonly: this.desactiver,
|
|
162
|
+
|
|
163
|
+
setup: editor => {
|
|
164
|
+
this._editor = editor
|
|
165
|
+
editor.on('init', () => {
|
|
166
|
+
//console.log('[Editeur] TinyMCE INIT done')
|
|
167
|
+
})
|
|
168
|
+
// Délégation d’événements pour intercepter les clics de liens
|
|
169
|
+
editor.on('click', e => {
|
|
170
|
+
const a = e.target?.closest?.('a')
|
|
171
|
+
if (!a) return
|
|
172
|
+
const href = a.getAttribute('href')
|
|
173
|
+
const target = a.getAttribute('target')
|
|
174
|
+
if (href) {
|
|
175
|
+
e.preventDefault()
|
|
176
|
+
openLink(href, target)
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
// ENTER sur un lien
|
|
181
|
+
editor.on('keydown', e => {
|
|
182
|
+
if (e.key !== 'Enter') return
|
|
183
|
+
const node = editor.selection?.getNode?.()
|
|
184
|
+
const a = node?.closest?.('a')
|
|
185
|
+
if (a) {
|
|
186
|
+
e.preventDefault()
|
|
187
|
+
openLink(a.getAttribute('href'), a.getAttribute('target'))
|
|
188
|
+
}
|
|
189
|
+
})
|
|
190
|
+
},
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// File picker uniquement si images permises
|
|
194
|
+
if (!this.interdireImage) {
|
|
195
|
+
opts.file_picker_callback = cb => {
|
|
196
|
+
const input = document.createElement('input')
|
|
197
|
+
input.type = 'file'
|
|
198
|
+
input.accept = 'image/*'
|
|
199
|
+
input.onchange = () => {
|
|
200
|
+
const file = input.files?.[0]
|
|
201
|
+
if (!file) return
|
|
202
|
+
|
|
203
|
+
const reader = new FileReader()
|
|
204
|
+
reader.onload = () => {
|
|
205
|
+
const base64 = String(reader.result).split(',')[1]
|
|
206
|
+
const editor = this._editor
|
|
207
|
+
const blobCache = editor?.editorUpload?.blobCache
|
|
208
|
+
if (!blobCache) return
|
|
209
|
+
|
|
210
|
+
const id = 'blobid' + Date.now()
|
|
211
|
+
const blobInfo = blobCache.create(id, file, base64)
|
|
212
|
+
const imageSize = blobInfo.blob().size / 1_000_000
|
|
213
|
+
if (imageSize > this.imageTailleMaxMo) {
|
|
214
|
+
alert('Fichier trop volumineux > ' + this.imageTailleMaxMo + ' Mo')
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
blobCache.add(blobInfo)
|
|
218
|
+
cb(blobInfo.blobUri(), { title: file.name })
|
|
219
|
+
}
|
|
220
|
+
reader.readAsDataURL(file)
|
|
221
|
+
}
|
|
222
|
+
input.click()
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return opts
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
watch: {
|
|
231
|
+
// Re-montage propre de l’éditeur dès qu’on touche aux props
|
|
232
|
+
interdireImage() {
|
|
233
|
+
this._scheduleReinit()
|
|
234
|
+
},
|
|
235
|
+
interdireRedimension() {
|
|
236
|
+
this._scheduleReinit()
|
|
237
|
+
},
|
|
238
|
+
toolbar() {
|
|
239
|
+
this._scheduleReinit()
|
|
240
|
+
},
|
|
241
|
+
imageTailleMaximale() {
|
|
242
|
+
this._scheduleReinit()
|
|
243
|
+
},
|
|
244
|
+
plugins() {
|
|
245
|
+
this._scheduleReinit()
|
|
246
|
+
},
|
|
247
|
+
cacherBarreOutils() {
|
|
248
|
+
this._scheduleReinit()
|
|
249
|
+
},
|
|
250
|
+
cacherBarreMenu() {
|
|
251
|
+
this._scheduleReinit()
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
value(v) {
|
|
255
|
+
this.editorValue = v
|
|
256
|
+
},
|
|
257
|
+
editorValue(v) {
|
|
258
|
+
this.$emit('input', v)
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
methods: {
|
|
263
|
+
_normalizeToolbar(tb) {
|
|
264
|
+
// Si la toolbar devient vide après nettoyage, on met au moins "undo redo"
|
|
265
|
+
const trimmed = (tb || '').trim()
|
|
266
|
+
return trimmed && trimmed !== '|' ? trimmed : 'undo redo'
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
_scheduleReinit() {
|
|
270
|
+
// coalesce toutes les demandes de réinit dans la même micro-tâche
|
|
271
|
+
if (this._reinitPending) return
|
|
272
|
+
this._reinitPending = true
|
|
273
|
+
queueMicrotask(async () => {
|
|
274
|
+
this._reinitPending = false
|
|
275
|
+
await this._reinitEditor()
|
|
276
|
+
})
|
|
277
|
+
},
|
|
278
|
+
|
|
279
|
+
async _reinitEditor() {
|
|
280
|
+
if (this._reinitLock) return
|
|
281
|
+
this._reinitLock = true
|
|
282
|
+
try {
|
|
283
|
+
// détruire proprement l’instance via l’instance elle-même
|
|
284
|
+
const ed = this._editor
|
|
285
|
+
if (ed) {
|
|
286
|
+
try {
|
|
287
|
+
ed.remove && ed.remove()
|
|
288
|
+
} catch (e) {
|
|
289
|
+
console.warn('[Editeur] editor.remove erreur', e)
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
this._editor = null
|
|
293
|
+
|
|
294
|
+
// 🔁 remount contrôlé
|
|
295
|
+
this.editorReady = false
|
|
296
|
+
await this.$nextTick()
|
|
297
|
+
this.editorReady = true
|
|
298
|
+
await this.$nextTick()
|
|
299
|
+
} finally {
|
|
300
|
+
this._reinitLock = false
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
|
|
304
|
+
surEvenementBlur() {
|
|
305
|
+
if (!this.desactiver) {
|
|
306
|
+
this.$emit('blur')
|
|
307
|
+
this.$emit('input', this.editorValue)
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
beforeUnmount() {
|
|
313
|
+
// Cleanup sûr lors du démontage du composant parent
|
|
314
|
+
try {
|
|
315
|
+
if (this._editor) tinymce.remove(this._editor)
|
|
316
|
+
} catch {}
|
|
317
|
+
this._editor = null
|
|
318
|
+
},
|
|
319
|
+
}
|
|
320
|
+
</script>
|
|
321
|
+
|
|
322
|
+
<style scoped>
|
|
323
|
+
.editor a {
|
|
324
|
+
cursor: pointer !important;
|
|
325
|
+
}
|
|
326
|
+
</style>
|
|
@@ -85,6 +85,12 @@
|
|
|
85
85
|
texte: string
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
interface EnteteEtatSecondaire {
|
|
89
|
+
afficher: boolean
|
|
90
|
+
couleur: string
|
|
91
|
+
texte: string
|
|
92
|
+
}
|
|
93
|
+
|
|
88
94
|
const router = useRouter()
|
|
89
95
|
|
|
90
96
|
const props = defineProps<{
|
|
@@ -92,6 +98,7 @@
|
|
|
92
98
|
soustitre?: string
|
|
93
99
|
retour?: string
|
|
94
100
|
etat?: EnteteEtat
|
|
101
|
+
etatSecondaire?: EnteteEtatSecondaire
|
|
95
102
|
}>()
|
|
96
103
|
|
|
97
104
|
const titreCol = ref(12)
|
|
@@ -107,6 +114,16 @@
|
|
|
107
114
|
)
|
|
108
115
|
})
|
|
109
116
|
|
|
117
|
+
const monEtatSecondaire = computed<EnteteEtatSecondaire>(() => {
|
|
118
|
+
return (
|
|
119
|
+
props.etatSecondaire ?? {
|
|
120
|
+
afficher: false,
|
|
121
|
+
couleur: 'primary',
|
|
122
|
+
texte: 'test',
|
|
123
|
+
}
|
|
124
|
+
)
|
|
125
|
+
})
|
|
126
|
+
|
|
110
127
|
function retournerMenu() {
|
|
111
128
|
if (props.retour) {
|
|
112
129
|
router.push({ name: props.retour })
|
|
@@ -1,149 +1,155 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<v-navigation-drawer
|
|
3
|
-
v-model="visible"
|
|
4
|
-
location="right"
|
|
5
|
-
temporary
|
|
6
|
-
class="pa-0 elevation-2"
|
|
7
|
-
:width="grosseurTiroir"
|
|
8
|
-
:persistent="persistant"
|
|
9
|
-
@keydown.esc="!persistant ? fermeture : ''"
|
|
10
|
-
@click:outside="!persistant ? fermeture : ''"
|
|
11
|
-
>
|
|
12
|
-
<v-card class="pa-0 ma-0 pl-8 pt-8">
|
|
13
|
-
<!-- Bouton en haut à droite -->
|
|
14
|
-
<v-btn
|
|
15
|
-
icon="mdi-close"
|
|
16
|
-
variant="text"
|
|
17
|
-
class="position-absolute couleurHover"
|
|
18
|
-
style="top: 5px; right: 5px"
|
|
19
|
-
@click="fermeture"
|
|
20
|
-
></v-btn>
|
|
21
|
-
|
|
22
|
-
<v-card-title
|
|
23
|
-
class="pa-0 ma-0 pb-6 text-wrap"
|
|
24
|
-
style="font-size: 24px"
|
|
25
|
-
>
|
|
26
|
-
<slot name="titre"></slot>
|
|
27
|
-
<div text-h5>{{ titre }}</div>
|
|
28
|
-
</v-card-title>
|
|
29
|
-
|
|
30
|
-
<v-card-text class="pa-0 ma-0 pb-6 pr-6">
|
|
31
|
-
<v-container>
|
|
32
|
-
<slot></slot>
|
|
33
|
-
<slot name="content"></slot>
|
|
34
|
-
</v-container>
|
|
35
|
-
</v-card-text>
|
|
36
|
-
<v-card-actions class="my-2 d-flex justify-end pr-6 pb-5">
|
|
37
|
-
<slot name="actions"></slot>
|
|
38
|
-
<v-btn
|
|
39
|
-
v-if="btnAnnuler"
|
|
40
|
-
color="primary"
|
|
41
|
-
variant="text"
|
|
42
|
-
:loading="operationEnCours"
|
|
43
|
-
@click="fermeture"
|
|
44
|
-
>
|
|
45
|
-
{{ props.btnAnnulerTexte ? props.btnAnnulerTexte : $t('csqc.bouton.annuler') }}
|
|
46
|
-
</v-btn>
|
|
47
|
-
|
|
48
|
-
<v-btn
|
|
49
|
-
v-if="btnOk"
|
|
50
|
-
class="Gouttiere"
|
|
51
|
-
color="primary"
|
|
52
|
-
variant="flat"
|
|
53
|
-
:loading="operationEnCours"
|
|
54
|
-
:disabled="btnOkDesactiver || operationEnCours"
|
|
55
|
-
@click="okBouton"
|
|
56
|
-
>
|
|
57
|
-
{{ props.btnOkTexte ? props.btnOkTexte : $t('csqc.bouton.ok') }}
|
|
58
|
-
</v-btn>
|
|
59
|
-
</v-card-actions>
|
|
60
|
-
</v-card>
|
|
61
|
-
</v-navigation-drawer>
|
|
62
|
-
</template>
|
|
63
|
-
<script lang="ts" setup>
|
|
64
|
-
import { ref, computed } from 'vue'
|
|
65
|
-
import { useDisplay } from 'vuetify'
|
|
66
|
-
|
|
67
|
-
const visible = ref(false)
|
|
68
|
-
const display = useDisplay()
|
|
69
|
-
|
|
70
|
-
// Déclaration des props
|
|
71
|
-
const props = defineProps({
|
|
72
|
-
titre: {
|
|
73
|
-
type: String,
|
|
74
|
-
default: '',
|
|
75
|
-
required: false,
|
|
76
|
-
},
|
|
77
|
-
btnAnnuler: {
|
|
78
|
-
type: Boolean,
|
|
79
|
-
default: true,
|
|
80
|
-
required: false,
|
|
81
|
-
},
|
|
82
|
-
btnOk: {
|
|
83
|
-
type: Boolean,
|
|
84
|
-
default: true,
|
|
85
|
-
required: false,
|
|
86
|
-
},
|
|
87
|
-
btnAnnulerTexte: {
|
|
88
|
-
type: String,
|
|
89
|
-
default: '',
|
|
90
|
-
required: false,
|
|
91
|
-
},
|
|
92
|
-
btnOkDesactiver: {
|
|
93
|
-
type: Boolean,
|
|
94
|
-
default: false,
|
|
95
|
-
required: false,
|
|
96
|
-
},
|
|
97
|
-
operationEnCours: { type: Boolean, default: false },
|
|
98
|
-
persistant: { type: Boolean, default: true },
|
|
99
|
-
btnOkTexte: {
|
|
100
|
-
type: String,
|
|
101
|
-
default: '',
|
|
102
|
-
required: false,
|
|
103
|
-
},
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
// Déclaration des événements
|
|
107
|
-
const emit = defineEmits(['fermer', 'ok'])
|
|
108
|
-
|
|
109
|
-
// Méthodes
|
|
110
|
-
const ouvrir = () => {
|
|
111
|
-
visible.value = true
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const fermer = () => {
|
|
115
|
-
visible.value = false
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const fermeture = () => {
|
|
119
|
-
emit('fermer')
|
|
120
|
-
fermer()
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const okBouton = () => {
|
|
124
|
-
emit('ok')
|
|
125
|
-
fermer()
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Calcul des tailles du tiroir en fonction du breakpoint
|
|
129
|
-
const grosseurTiroir = computed(() => {
|
|
130
|
-
switch (display.name.value) {
|
|
131
|
-
case 'xs':
|
|
132
|
-
return ''
|
|
133
|
-
case 'sm':
|
|
134
|
-
return 0.95 * display.width.value
|
|
135
|
-
case 'md':
|
|
136
|
-
return 0.8 * display.width.value
|
|
137
|
-
case 'lg':
|
|
138
|
-
return 0.7 * display.width.value
|
|
139
|
-
case 'xl':
|
|
140
|
-
return 0.6 * display.width.value
|
|
141
|
-
case 'xxl':
|
|
142
|
-
return 0.5 * display.width.value
|
|
143
|
-
default:
|
|
144
|
-
return ''
|
|
145
|
-
}
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
defineExpose({ ouvrir, fermer })
|
|
149
|
-
</script>
|
|
1
|
+
<template>
|
|
2
|
+
<v-navigation-drawer
|
|
3
|
+
v-model="visible"
|
|
4
|
+
location="right"
|
|
5
|
+
temporary
|
|
6
|
+
class="pa-0 elevation-2 csqc-ligneBleue"
|
|
7
|
+
:width="grosseurTiroir"
|
|
8
|
+
:persistent="persistant"
|
|
9
|
+
@keydown.esc="!persistant ? fermeture : ''"
|
|
10
|
+
@click:outside="!persistant ? fermeture : ''"
|
|
11
|
+
>
|
|
12
|
+
<v-card class="pa-0 ma-0 pl-8 pt-8">
|
|
13
|
+
<!-- Bouton en haut à droite -->
|
|
14
|
+
<v-btn
|
|
15
|
+
icon="mdi-close"
|
|
16
|
+
variant="text"
|
|
17
|
+
class="position-absolute couleurHover"
|
|
18
|
+
style="top: 5px; right: 5px"
|
|
19
|
+
@click="fermeture"
|
|
20
|
+
></v-btn>
|
|
21
|
+
|
|
22
|
+
<v-card-title
|
|
23
|
+
class="pa-0 ma-0 pb-6 text-wrap"
|
|
24
|
+
style="font-size: 24px"
|
|
25
|
+
>
|
|
26
|
+
<slot name="titre"></slot>
|
|
27
|
+
<div text-h5>{{ titre }}</div>
|
|
28
|
+
</v-card-title>
|
|
29
|
+
|
|
30
|
+
<v-card-text class="pa-0 ma-0 pb-6 pr-6">
|
|
31
|
+
<v-container>
|
|
32
|
+
<slot></slot>
|
|
33
|
+
<slot name="content"></slot>
|
|
34
|
+
</v-container>
|
|
35
|
+
</v-card-text>
|
|
36
|
+
<v-card-actions class="my-2 d-flex justify-end pr-6 pb-5">
|
|
37
|
+
<slot name="actions"></slot>
|
|
38
|
+
<v-btn
|
|
39
|
+
v-if="btnAnnuler"
|
|
40
|
+
color="primary"
|
|
41
|
+
variant="text"
|
|
42
|
+
:loading="operationEnCours"
|
|
43
|
+
@click="fermeture"
|
|
44
|
+
>
|
|
45
|
+
{{ props.btnAnnulerTexte ? props.btnAnnulerTexte : $t('csqc.bouton.annuler') }}
|
|
46
|
+
</v-btn>
|
|
47
|
+
|
|
48
|
+
<v-btn
|
|
49
|
+
v-if="btnOk"
|
|
50
|
+
class="Gouttiere"
|
|
51
|
+
color="primary"
|
|
52
|
+
variant="flat"
|
|
53
|
+
:loading="operationEnCours"
|
|
54
|
+
:disabled="btnOkDesactiver || operationEnCours"
|
|
55
|
+
@click="okBouton"
|
|
56
|
+
>
|
|
57
|
+
{{ props.btnOkTexte ? props.btnOkTexte : $t('csqc.bouton.ok') }}
|
|
58
|
+
</v-btn>
|
|
59
|
+
</v-card-actions>
|
|
60
|
+
</v-card>
|
|
61
|
+
</v-navigation-drawer>
|
|
62
|
+
</template>
|
|
63
|
+
<script lang="ts" setup>
|
|
64
|
+
import { ref, computed } from 'vue'
|
|
65
|
+
import { useDisplay } from 'vuetify'
|
|
66
|
+
|
|
67
|
+
const visible = ref(false)
|
|
68
|
+
const display = useDisplay()
|
|
69
|
+
|
|
70
|
+
// Déclaration des props
|
|
71
|
+
const props = defineProps({
|
|
72
|
+
titre: {
|
|
73
|
+
type: String,
|
|
74
|
+
default: '',
|
|
75
|
+
required: false,
|
|
76
|
+
},
|
|
77
|
+
btnAnnuler: {
|
|
78
|
+
type: Boolean,
|
|
79
|
+
default: true,
|
|
80
|
+
required: false,
|
|
81
|
+
},
|
|
82
|
+
btnOk: {
|
|
83
|
+
type: Boolean,
|
|
84
|
+
default: true,
|
|
85
|
+
required: false,
|
|
86
|
+
},
|
|
87
|
+
btnAnnulerTexte: {
|
|
88
|
+
type: String,
|
|
89
|
+
default: '',
|
|
90
|
+
required: false,
|
|
91
|
+
},
|
|
92
|
+
btnOkDesactiver: {
|
|
93
|
+
type: Boolean,
|
|
94
|
+
default: false,
|
|
95
|
+
required: false,
|
|
96
|
+
},
|
|
97
|
+
operationEnCours: { type: Boolean, default: false },
|
|
98
|
+
persistant: { type: Boolean, default: true },
|
|
99
|
+
btnOkTexte: {
|
|
100
|
+
type: String,
|
|
101
|
+
default: '',
|
|
102
|
+
required: false,
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
// Déclaration des événements
|
|
107
|
+
const emit = defineEmits(['fermer', 'ok'])
|
|
108
|
+
|
|
109
|
+
// Méthodes
|
|
110
|
+
const ouvrir = () => {
|
|
111
|
+
visible.value = true
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const fermer = () => {
|
|
115
|
+
visible.value = false
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const fermeture = () => {
|
|
119
|
+
emit('fermer')
|
|
120
|
+
fermer()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const okBouton = () => {
|
|
124
|
+
emit('ok')
|
|
125
|
+
fermer()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Calcul des tailles du tiroir en fonction du breakpoint
|
|
129
|
+
const grosseurTiroir = computed(() => {
|
|
130
|
+
switch (display.name.value) {
|
|
131
|
+
case 'xs':
|
|
132
|
+
return ''
|
|
133
|
+
case 'sm':
|
|
134
|
+
return 0.95 * display.width.value
|
|
135
|
+
case 'md':
|
|
136
|
+
return 0.8 * display.width.value
|
|
137
|
+
case 'lg':
|
|
138
|
+
return 0.7 * display.width.value
|
|
139
|
+
case 'xl':
|
|
140
|
+
return 0.6 * display.width.value
|
|
141
|
+
case 'xxl':
|
|
142
|
+
return 0.5 * display.width.value
|
|
143
|
+
default:
|
|
144
|
+
return ''
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
defineExpose({ ouvrir, fermer })
|
|
149
|
+
</script>
|
|
150
|
+
|
|
151
|
+
<style>
|
|
152
|
+
.csqc-ligneBleue {
|
|
153
|
+
border-left: 30px #095797 solid !important;
|
|
154
|
+
}
|
|
155
|
+
</style>
|
package/index.ts
CHANGED
|
@@ -1,70 +1,71 @@
|
|
|
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 csqcTable from './composants/csqcTable/csqcTable.vue'
|
|
12
|
-
import csqcCodeBudgetaire from './composants/codeBudgetaireGenerique.vue'
|
|
13
|
-
//import csqcChaise from './composants/csqcChaise/chaiseConteneur.vue'
|
|
14
|
-
import csqcAide from './composants/csqcAide.vue'
|
|
15
|
-
import csqcEntete from './composants/csqcEntete.vue'
|
|
16
|
-
import csqcTexteBilingue from './composants/csqcTexteBilingue.vue'
|
|
17
|
-
|
|
18
|
-
//import validateurs from './validateurs'
|
|
19
|
-
|
|
20
|
-
import Utilisateur from './modeles/utilisateur'
|
|
21
|
-
import Unite from './modeles/unite'
|
|
22
|
-
import Intervention from './modeles/intervention'
|
|
23
|
-
import DroitIntervention from './modeles/droitIntervention'
|
|
24
|
-
import Role from './modeles/role'
|
|
25
|
-
import RoleMin from './modeles/roleMin'
|
|
26
|
-
import GroupeCE from './modeles/groupeCE'
|
|
27
|
-
import NotificationGabaritDefaut from './modeles/notificationGabaritDefaut'
|
|
28
|
-
import modeleSnackbar from './modeles/composants/snackbar'
|
|
29
|
-
import modeleDatatableColonne from './modeles/composants/datatableColonne'
|
|
30
|
-
import apiReponse from './modeles/apiReponse'
|
|
31
|
-
import data from './modeles/data'
|
|
32
|
-
import response from './modeles/response'
|
|
33
|
-
|
|
34
|
-
import csqcEn from './locales/en.json'
|
|
35
|
-
import csqcFr from './locales/fr.json'
|
|
36
|
-
|
|
37
|
-
export {
|
|
38
|
-
csqcFr,
|
|
39
|
-
csqcEn,
|
|
40
|
-
csqcAlerteErreur,
|
|
41
|
-
csqcDialogue,
|
|
42
|
-
csqcConfirmation,
|
|
43
|
-
csqcOptionSwitch,
|
|
44
|
-
csqcRecherche,
|
|
45
|
-
csqcSnackbar,
|
|
46
|
-
csqcTable,
|
|
47
|
-
csqcTiroir,
|
|
48
|
-
csqcMenu,
|
|
49
|
-
csqcCodeBudgetaire,
|
|
50
|
-
//csqcChaise,
|
|
51
|
-
pivFooter,
|
|
52
|
-
pivEntete,
|
|
53
|
-
csqcAide,
|
|
54
|
-
csqcEntete,
|
|
55
|
-
csqcTexteBilingue,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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 csqcTable from './composants/csqcTable/csqcTable.vue'
|
|
12
|
+
import csqcCodeBudgetaire from './composants/codeBudgetaireGenerique.vue'
|
|
13
|
+
//import csqcChaise from './composants/csqcChaise/chaiseConteneur.vue'
|
|
14
|
+
import csqcAide from './composants/csqcAide.vue'
|
|
15
|
+
import csqcEntete from './composants/csqcEntete.vue'
|
|
16
|
+
import csqcTexteBilingue from './composants/csqcTexteBilingue.vue'
|
|
17
|
+
import csqcEditeurTexteRiche from './composants/csqcEditeurTexteRiche.vue'
|
|
18
|
+
//import validateurs from './validateurs'
|
|
19
|
+
|
|
20
|
+
import Utilisateur from './modeles/utilisateur'
|
|
21
|
+
import Unite from './modeles/unite'
|
|
22
|
+
import Intervention from './modeles/intervention'
|
|
23
|
+
import DroitIntervention from './modeles/droitIntervention'
|
|
24
|
+
import Role from './modeles/role'
|
|
25
|
+
import RoleMin from './modeles/roleMin'
|
|
26
|
+
import GroupeCE from './modeles/groupeCE'
|
|
27
|
+
import NotificationGabaritDefaut from './modeles/notificationGabaritDefaut'
|
|
28
|
+
import modeleSnackbar from './modeles/composants/snackbar'
|
|
29
|
+
import modeleDatatableColonne from './modeles/composants/datatableColonne'
|
|
30
|
+
import apiReponse from './modeles/apiReponse'
|
|
31
|
+
import data from './modeles/data'
|
|
32
|
+
import response from './modeles/response'
|
|
33
|
+
|
|
34
|
+
import csqcEn from './locales/en.json'
|
|
35
|
+
import csqcFr from './locales/fr.json'
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
csqcFr,
|
|
39
|
+
csqcEn,
|
|
40
|
+
csqcAlerteErreur,
|
|
41
|
+
csqcDialogue,
|
|
42
|
+
csqcConfirmation,
|
|
43
|
+
csqcOptionSwitch,
|
|
44
|
+
csqcRecherche,
|
|
45
|
+
csqcSnackbar,
|
|
46
|
+
csqcTable,
|
|
47
|
+
csqcTiroir,
|
|
48
|
+
csqcMenu,
|
|
49
|
+
csqcCodeBudgetaire,
|
|
50
|
+
//csqcChaise,
|
|
51
|
+
pivFooter,
|
|
52
|
+
pivEntete,
|
|
53
|
+
csqcAide,
|
|
54
|
+
csqcEntete,
|
|
55
|
+
csqcTexteBilingue,
|
|
56
|
+
csqcEditeurTexteRiche,
|
|
57
|
+
//validateurs,
|
|
58
|
+
Utilisateur,
|
|
59
|
+
Unite,
|
|
60
|
+
Intervention,
|
|
61
|
+
modeleSnackbar,
|
|
62
|
+
modeleDatatableColonne,
|
|
63
|
+
apiReponse,
|
|
64
|
+
data,
|
|
65
|
+
response,
|
|
66
|
+
NotificationGabaritDefaut,
|
|
67
|
+
DroitIntervention,
|
|
68
|
+
Role,
|
|
69
|
+
RoleMin,
|
|
70
|
+
GroupeCE,
|
|
71
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codevdesign",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.69",
|
|
4
4
|
"description": "Composants Vuetify 3 pour les projets Codev",
|
|
5
5
|
"files": [
|
|
6
6
|
"./**/*.vue",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"build": "vite build"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"vuetify": "^3.
|
|
16
|
+
"vuetify": "^3.8.0",
|
|
17
17
|
"vue-i18n": "^11.0.0",
|
|
18
18
|
"@e965/xlsx": "^0.20.3"
|
|
19
19
|
},
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"@types/node": "^22.13.5",
|
|
22
22
|
"@vitejs/plugin-vue": "^5.2.1",
|
|
23
23
|
"typescript": "^5.7.3",
|
|
24
|
-
"vite": "^
|
|
24
|
+
"vite": "^7.0.0"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"vue": "^3.5.0"
|