codevdesign 0.0.69 → 0.0.71
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.
|
@@ -6,28 +6,30 @@
|
|
|
6
6
|
v-model="editorValue"
|
|
7
7
|
:init="initOptions"
|
|
8
8
|
:disabled="desactiver"
|
|
9
|
-
@blur="
|
|
10
|
-
@keydown="
|
|
11
|
-
@
|
|
12
|
-
@change="surEvenementBlur"
|
|
9
|
+
@blur="onBlur"
|
|
10
|
+
@keydown="onUserActivity"
|
|
11
|
+
@change="onUserActivity"
|
|
13
12
|
license-key="gpl"
|
|
14
13
|
/>
|
|
15
14
|
</div>
|
|
16
15
|
</template>
|
|
17
16
|
|
|
18
17
|
<script>
|
|
19
|
-
/* Vue 3 - Options API */
|
|
20
18
|
import Editor from '@tinymce/tinymce-vue'
|
|
21
19
|
import * as tinymce from 'tinymce/tinymce.js'
|
|
20
|
+
|
|
21
|
+
// Core/skins/thème
|
|
22
22
|
import 'tinymce/icons/default/icons.min.js'
|
|
23
23
|
import 'tinymce/themes/silver/theme.min.js'
|
|
24
24
|
import 'tinymce/models/dom/model.min.js'
|
|
25
25
|
import 'tinymce/skins/ui/oxide/skin.js'
|
|
26
26
|
import 'tinymce/skins/ui/oxide/content.js'
|
|
27
27
|
import 'tinymce/skins/content/default/content.js'
|
|
28
|
+
|
|
29
|
+
// Langue
|
|
28
30
|
import 'tinymce-i18n/langs7/fr_FR'
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
// Plugins
|
|
31
33
|
import 'tinymce/plugins/autoresize/plugin.min.js'
|
|
32
34
|
import 'tinymce/plugins/advlist/plugin.min.js'
|
|
33
35
|
import 'tinymce/plugins/lists/plugin.min.js'
|
|
@@ -35,7 +37,7 @@
|
|
|
35
37
|
import 'tinymce/plugins/autolink/plugin.min.js'
|
|
36
38
|
import 'tinymce/plugins/fullscreen/plugin.min.js'
|
|
37
39
|
import 'tinymce/plugins/table/plugin.min.js'
|
|
38
|
-
import 'tinymce/plugins/image/plugin.min.js'
|
|
40
|
+
import 'tinymce/plugins/image/plugin.min.js'
|
|
39
41
|
import 'tinymce/plugins/emoticons/plugin.min.js'
|
|
40
42
|
import 'tinymce/plugins/emoticons/js/emojis.min.js'
|
|
41
43
|
import 'tinymce/plugins/code/plugin.min.js'
|
|
@@ -56,8 +58,8 @@
|
|
|
56
58
|
components: { Editor },
|
|
57
59
|
|
|
58
60
|
props: {
|
|
61
|
+
modelValue: { type: String, default: '' },
|
|
59
62
|
langue: { type: String, default: 'fr_FR' },
|
|
60
|
-
value: { type: String, default: '' },
|
|
61
63
|
interdireRedimension: { type: Boolean, default: false },
|
|
62
64
|
desactiver: { type: Boolean, default: false },
|
|
63
65
|
plugins: {
|
|
@@ -65,7 +67,7 @@
|
|
|
65
67
|
default: 'autoresize table image link fullscreen advlist lists emoticons autolink code',
|
|
66
68
|
},
|
|
67
69
|
interdireImage: { type: Boolean, default: false },
|
|
68
|
-
imageTailleMaximale: { type: Number, default: 5 }, //
|
|
70
|
+
imageTailleMaximale: { type: Number, default: 5 }, // Mo
|
|
69
71
|
cacherBarreMenu: { type: Boolean, default: false },
|
|
70
72
|
cacherBarreOutils: { type: Boolean, default: false },
|
|
71
73
|
toolbar: {
|
|
@@ -77,95 +79,88 @@
|
|
|
77
79
|
},
|
|
78
80
|
},
|
|
79
81
|
|
|
82
|
+
emits: ['update:modelValue', 'blur'],
|
|
83
|
+
|
|
80
84
|
data() {
|
|
81
85
|
return {
|
|
82
86
|
editorReady: true,
|
|
83
87
|
_editor: null,
|
|
84
|
-
editorValue: this.
|
|
88
|
+
editorValue: this.modelValue, // lié au v-model
|
|
85
89
|
_reinitPending: false,
|
|
86
90
|
_reinitLock: false,
|
|
87
91
|
}
|
|
88
92
|
},
|
|
89
93
|
|
|
90
94
|
computed: {
|
|
91
|
-
// <= ICI on force un plafond d'image à 100 Mo (et on gère les valeurs non numériques)
|
|
92
95
|
imageTailleMaxMo() {
|
|
93
96
|
const n = Number(this.imageTailleMaximale)
|
|
94
97
|
if (!Number.isFinite(n)) return 5
|
|
95
98
|
return Math.min(100, Math.max(0, n))
|
|
96
99
|
},
|
|
97
100
|
|
|
98
|
-
// Nettoie la toolbar en retirant "image" et ses séparateurs orphelins
|
|
99
101
|
toolbarEffective() {
|
|
100
102
|
if (this.cacherBarreOutils) return false
|
|
101
103
|
const raw = Array.isArray(this.toolbar) ? this.toolbar.join(' | ') : this.toolbar
|
|
102
104
|
if (!this.interdireImage) return this._normalizeToolbar(raw)
|
|
103
105
|
|
|
104
106
|
const sansImage = raw
|
|
105
|
-
.replace(/\bimage\b/g, '')
|
|
106
|
-
.replace(/\s*\|\s*/g, ' | ')
|
|
107
|
-
.replace(/(\s*\|\s*){2,}/g, ' | ')
|
|
108
|
-
.replace(/^\s*\|\s*|\s*\|\s*$/g, '')
|
|
109
|
-
.replace(/\s{2,}/g, ' ')
|
|
107
|
+
.replace(/\bimage\b/g, '')
|
|
108
|
+
.replace(/\s*\|\s*/g, ' | ')
|
|
109
|
+
.replace(/(\s*\|\s*){2,}/g, ' | ')
|
|
110
|
+
.replace(/^\s*\|\s*|\s*\|\s*$/g, '')
|
|
111
|
+
.replace(/\s{2,}/g, ' ')
|
|
110
112
|
.trim()
|
|
111
113
|
|
|
112
114
|
return this._normalizeToolbar(sansImage)
|
|
113
115
|
},
|
|
114
116
|
|
|
115
|
-
// Plugins effectifs : retire "image" si interdireImage = true
|
|
116
117
|
pluginsEffective() {
|
|
117
118
|
const list = (Array.isArray(this.plugins) ? this.plugins : this.plugins.split(/\s+/))
|
|
118
119
|
.map(p => p.trim())
|
|
119
120
|
.filter(Boolean)
|
|
120
|
-
|
|
121
121
|
const filtered = this.interdireImage ? list.filter(p => p !== 'image') : list
|
|
122
122
|
return filtered.join(' ')
|
|
123
123
|
},
|
|
124
124
|
|
|
125
|
-
// Options TinyMCE réactives
|
|
126
125
|
initOptions() {
|
|
127
126
|
const opts = {
|
|
128
127
|
autolink: !this.desactiver,
|
|
129
128
|
autoresize: true,
|
|
129
|
+
automatic_uploads: !this.interdireImage,
|
|
130
130
|
branding: false,
|
|
131
131
|
browser_spellcheck: true,
|
|
132
132
|
content_css: 'default',
|
|
133
133
|
deprecation_warnings: false,
|
|
134
134
|
emoticons_database: 'emojis',
|
|
135
|
-
//height: this.height,
|
|
136
|
-
//min_heignt: 100,
|
|
137
|
-
//max_height: 800,
|
|
138
135
|
highlight_on_focus: false,
|
|
139
|
-
|
|
136
|
+
image_dimensions: !this.interdireImage,
|
|
137
|
+
language: this.langue === 'en' ? 'en_US' : 'fr_FR',
|
|
140
138
|
license_key: 'gpl',
|
|
141
139
|
menubar: !this.cacherBarreMenu,
|
|
140
|
+
object_resizing: !this.interdireImage,
|
|
141
|
+
paste_data_images: !this.interdireImage,
|
|
142
|
+
plugins: this.pluginsEffective,
|
|
142
143
|
promotion: false,
|
|
144
|
+
readonly: this.desactiver,
|
|
143
145
|
resize: !this.interdireRedimension,
|
|
146
|
+
resize_img_proportional: !this.interdireImage,
|
|
144
147
|
skin_url: 'default',
|
|
145
148
|
statusbar: this.cacherBarreOutils === false,
|
|
146
149
|
theme_advanced_resizing: true,
|
|
147
150
|
theme_advanced_resizing_use_cookie: false,
|
|
148
|
-
|
|
149
|
-
// Gestion images selon interdireImage
|
|
150
|
-
plugins: this.pluginsEffective,
|
|
151
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
152
|
|
|
161
|
-
|
|
153
|
+
// --- File picker seulement si images permises ---
|
|
154
|
+
...(this.interdireImage ? {} : { file_picker_types: 'image' }),
|
|
162
155
|
|
|
163
156
|
setup: editor => {
|
|
164
157
|
this._editor = editor
|
|
158
|
+
|
|
165
159
|
editor.on('init', () => {
|
|
166
|
-
//
|
|
160
|
+
// init ok
|
|
167
161
|
})
|
|
168
|
-
|
|
162
|
+
|
|
163
|
+
// Intercepter clics liens
|
|
169
164
|
editor.on('click', e => {
|
|
170
165
|
const a = e.target?.closest?.('a')
|
|
171
166
|
if (!a) return
|
|
@@ -190,7 +185,6 @@
|
|
|
190
185
|
},
|
|
191
186
|
}
|
|
192
187
|
|
|
193
|
-
// File picker uniquement si images permises
|
|
194
188
|
if (!this.interdireImage) {
|
|
195
189
|
opts.file_picker_callback = cb => {
|
|
196
190
|
const input = document.createElement('input')
|
|
@@ -228,7 +222,17 @@
|
|
|
228
222
|
},
|
|
229
223
|
|
|
230
224
|
watch: {
|
|
231
|
-
|
|
225
|
+
/* v-model (parent -> enfant) */
|
|
226
|
+
modelValue(v) {
|
|
227
|
+
this.editorValue = v
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
/* v-model (enfant -> parent) */
|
|
231
|
+
editorValue(v) {
|
|
232
|
+
this.$emit('update:modelValue', v)
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
// Re-montage propre sur changements des props
|
|
232
236
|
interdireImage() {
|
|
233
237
|
this._scheduleReinit()
|
|
234
238
|
},
|
|
@@ -250,24 +254,20 @@
|
|
|
250
254
|
cacherBarreMenu() {
|
|
251
255
|
this._scheduleReinit()
|
|
252
256
|
},
|
|
253
|
-
|
|
254
|
-
value(v) {
|
|
255
|
-
this.editorValue = v
|
|
256
|
-
},
|
|
257
|
-
editorValue(v) {
|
|
258
|
-
this.$emit('input', v)
|
|
259
|
-
},
|
|
260
257
|
},
|
|
261
258
|
|
|
262
259
|
methods: {
|
|
260
|
+
onBlur() {
|
|
261
|
+
if (!this.desactiver) this.$emit('blur')
|
|
262
|
+
},
|
|
263
|
+
onUserActivity() {},
|
|
264
|
+
|
|
263
265
|
_normalizeToolbar(tb) {
|
|
264
|
-
// Si la toolbar devient vide après nettoyage, on met au moins "undo redo"
|
|
265
266
|
const trimmed = (tb || '').trim()
|
|
266
267
|
return trimmed && trimmed !== '|' ? trimmed : 'undo redo'
|
|
267
268
|
},
|
|
268
269
|
|
|
269
270
|
_scheduleReinit() {
|
|
270
|
-
// coalesce toutes les demandes de réinit dans la même micro-tâche
|
|
271
271
|
if (this._reinitPending) return
|
|
272
272
|
this._reinitPending = true
|
|
273
273
|
queueMicrotask(async () => {
|
|
@@ -280,18 +280,17 @@
|
|
|
280
280
|
if (this._reinitLock) return
|
|
281
281
|
this._reinitLock = true
|
|
282
282
|
try {
|
|
283
|
-
// détruire proprement l’instance via l’instance elle-même
|
|
284
283
|
const ed = this._editor
|
|
285
|
-
if (ed) {
|
|
284
|
+
if (ed && typeof ed.remove === 'function') {
|
|
286
285
|
try {
|
|
287
|
-
ed.remove
|
|
286
|
+
ed.remove()
|
|
288
287
|
} catch (e) {
|
|
289
288
|
console.warn('[Editeur] editor.remove erreur', e)
|
|
290
289
|
}
|
|
291
290
|
}
|
|
292
291
|
this._editor = null
|
|
293
292
|
|
|
294
|
-
//
|
|
293
|
+
// remount contrôlé
|
|
295
294
|
this.editorReady = false
|
|
296
295
|
await this.$nextTick()
|
|
297
296
|
this.editorReady = true
|
|
@@ -300,19 +299,12 @@
|
|
|
300
299
|
this._reinitLock = false
|
|
301
300
|
}
|
|
302
301
|
},
|
|
303
|
-
|
|
304
|
-
surEvenementBlur() {
|
|
305
|
-
if (!this.desactiver) {
|
|
306
|
-
this.$emit('blur')
|
|
307
|
-
this.$emit('input', this.editorValue)
|
|
308
|
-
}
|
|
309
|
-
},
|
|
310
302
|
},
|
|
311
303
|
|
|
312
304
|
beforeUnmount() {
|
|
313
|
-
// Cleanup sûr lors du démontage du composant parent
|
|
314
305
|
try {
|
|
315
|
-
|
|
306
|
+
const ed = this._editor
|
|
307
|
+
if (ed && typeof ed.remove === 'function') ed.remove()
|
|
316
308
|
} catch {}
|
|
317
309
|
this._editor = null
|
|
318
310
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codevdesign",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.71",
|
|
4
4
|
"description": "Composants Vuetify 3 pour les projets Codev",
|
|
5
5
|
"files": [
|
|
6
6
|
"./**/*.vue",
|
|
@@ -15,7 +15,10 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"vuetify": "^3.8.0",
|
|
17
17
|
"vue-i18n": "^11.0.0",
|
|
18
|
-
"@e965/xlsx": "^0.20.3"
|
|
18
|
+
"@e965/xlsx": "^0.20.3",
|
|
19
|
+
"tinymce": "^8.0.2",
|
|
20
|
+
"tinymce-i18n": "^25.9.1",
|
|
21
|
+
"@tinymce/tinymce-vue": "^6.3.0"
|
|
19
22
|
},
|
|
20
23
|
"devDependencies": {
|
|
21
24
|
"@types/node": "^22.13.5",
|