codevdesign 0.0.70 → 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="surEvenementBlur"
10
- @keydown="surEvenementBlur"
11
- @input="surEvenementBlur"
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
- /* Plugins */
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' // activé/désactivé via "pluginsEffective"
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 }, // en Mo
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.value,
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, '') // 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
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
- language: this.langue == 'en' ? 'en_US' : 'fr_FR',
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
- readonly: this.desactiver,
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
- //console.log('[Editeur] TinyMCE INIT done')
160
+ // init ok
167
161
  })
168
- // Délégation d’événements pour intercepter les clics de liens
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
- // Re-montage propre de l’éditeur dès qu’on touche aux props
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 && 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
- // 🔁 remount contrôlé
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
- if (this._editor) tinymce.remove(this._editor)
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.70",
3
+ "version": "0.0.71",
4
4
  "description": "Composants Vuetify 3 pour les projets Codev",
5
5
  "files": [
6
6
  "./**/*.vue",
@@ -16,7 +16,7 @@
16
16
  "vuetify": "^3.8.0",
17
17
  "vue-i18n": "^11.0.0",
18
18
  "@e965/xlsx": "^0.20.3",
19
- "tinymce": "^7.9.1",
19
+ "tinymce": "^8.0.2",
20
20
  "tinymce-i18n": "^25.9.1",
21
21
  "@tinymce/tinymce-vue": "^6.3.0"
22
22
  },