@tiny-mce-umbraco/backoffice 17.3.4 → 17.6.2
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/dist-cms/README.md +1 -0
- package/dist-cms/components/input-tiny-mce/input-tiny-mce.defaults.d.ts.map +1 -1
- package/dist-cms/components/input-tiny-mce/input-tiny-mce.defaults.js +175 -6
- package/dist-cms/components/input-tiny-mce/input-tiny-mce.element.d.ts +4 -1
- package/dist-cms/components/input-tiny-mce/input-tiny-mce.element.d.ts.map +1 -1
- package/dist-cms/components/input-tiny-mce/input-tiny-mce.element.js +97 -12
- package/dist-cms/components/input-tiny-mce/shadow-dom-selection.d.ts +16 -0
- package/dist-cms/components/input-tiny-mce/shadow-dom-selection.d.ts.map +1 -0
- package/dist-cms/components/input-tiny-mce/shadow-dom-selection.js +112 -0
- package/dist-cms/components/stylesheet-rule-input/stylesheet-rule-input.element.d.ts +1 -1
- package/dist-cms/entry-points/blob-download-router-fix.d.ts +56 -0
- package/dist-cms/entry-points/blob-download-router-fix.d.ts.map +1 -0
- package/dist-cms/entry-points/blob-download-router-fix.js +81 -0
- package/dist-cms/entry-points/entry-point.d.ts.map +1 -1
- package/dist-cms/entry-points/entry-point.js +7 -1
- package/dist-cms/localizations/ar.d.ts.map +1 -1
- package/dist-cms/localizations/ar.js +3 -0
- package/dist-cms/localizations/bs.d.ts.map +1 -1
- package/dist-cms/localizations/bs.js +3 -0
- package/dist-cms/localizations/cy.d.ts.map +1 -1
- package/dist-cms/localizations/cy.js +3 -0
- package/dist-cms/localizations/da.d.ts.map +1 -1
- package/dist-cms/localizations/da.js +4 -0
- package/dist-cms/localizations/en.d.ts.map +1 -1
- package/dist-cms/localizations/en.js +4 -0
- package/dist-cms/localizations/hr.d.ts.map +1 -1
- package/dist-cms/localizations/hr.js +3 -0
- package/dist-cms/localizations/it.d.ts.map +1 -1
- package/dist-cms/localizations/it.js +3 -0
- package/dist-cms/localizations/manifests.d.ts.map +1 -1
- package/dist-cms/localizations/manifests.js +24 -24
- package/dist-cms/localizations/nl.d.ts.map +1 -1
- package/dist-cms/localizations/nl.js +3 -0
- package/dist-cms/localizations/tr.d.ts.map +1 -1
- package/dist-cms/localizations/tr.js +3 -0
- package/dist-cms/plugins/manifests.d.ts.map +1 -1
- package/dist-cms/plugins/manifests.js +17 -0
- package/dist-cms/plugins/tiny-mce-block-picker.plugin.d.ts.map +1 -1
- package/dist-cms/plugins/tiny-mce-block-picker.plugin.js +20 -2
- package/dist-cms/plugins/tiny-mce-embeddedmedia.plugin.d.ts.map +1 -1
- package/dist-cms/plugins/tiny-mce-embeddedmedia.plugin.js +1 -0
- package/dist-cms/plugins/tiny-mce-mediapicker.plugin.d.ts.map +1 -1
- package/dist-cms/plugins/tiny-mce-mediapicker.plugin.js +127 -43
- package/dist-cms/property-editors/configEditor/property-editor-ui-tiny-mce-config-editor.element.d.ts.map +1 -1
- package/dist-cms/property-editors/configEditor/property-editor-ui-tiny-mce-config-editor.element.js +0 -1
- package/dist-cms/property-editors/manifests.js +3 -3
- package/dist-cms/property-editors/plugin/property-editor-ui-tiny-mce-plugin-configuration.element.d.ts.map +1 -1
- package/dist-cms/property-editors/plugin/property-editor-ui-tiny-mce-plugin-configuration.element.js +15 -5
- package/dist-cms/property-editors/tiny-mce/manifests.d.ts.map +1 -1
- package/dist-cms/property-editors/tiny-mce/manifests.js +2 -1
- package/dist-cms/utils/index.d.ts +15 -5
- package/dist-cms/utils/index.d.ts.map +1 -1
- package/dist-cms/utils/index.js +27 -5
- package/package.json +6 -6
|
@@ -9,10 +9,10 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var _UmbTinyMceMediaPickerPlugin_instances, _UmbTinyMceMediaPickerPlugin_modalManager, _UmbTinyMceMediaPickerPlugin_temporaryFileRepository, _UmbTinyMceMediaPickerPlugin_onAction, _UmbTinyMceMediaPickerPlugin_showMediaPicker, _UmbTinyMceMediaPickerPlugin_showMediaCaptionAltText, _UmbTinyMceMediaPickerPlugin_insertInEditor, _UmbTinyMceMediaPickerPlugin_uploadImageHandler;
|
|
12
|
+
var _UmbTinyMceMediaPickerPlugin_instances, _UmbTinyMceMediaPickerPlugin_modalManager, _UmbTinyMceMediaPickerPlugin_temporaryFileRepository, _UmbTinyMceMediaPickerPlugin_allowedMediaTypeIds_get, _UmbTinyMceMediaPickerPlugin_resolveExistingMedia, _UmbTinyMceMediaPickerPlugin_onAction, _UmbTinyMceMediaPickerPlugin_parseDimension, _UmbTinyMceMediaPickerPlugin_showMediaPicker, _UmbTinyMceMediaPickerPlugin_showMediaCaptionAltText, _UmbTinyMceMediaPickerPlugin_insertInEditor, _UmbTinyMceMediaPickerPlugin_uploadImageHandler;
|
|
13
13
|
import { sizeImageInEditor, uploadBlobImages } from '@tiny-mce-umbraco/backoffice/core';
|
|
14
14
|
import { UmbTinyMcePluginBase } from '@tiny-mce-umbraco/backoffice/core';
|
|
15
|
-
import { getGuidFromUdi } from '@umbraco-cms/backoffice/utils';
|
|
15
|
+
import { getGuidFromUdi, splitStringToArray } from '@umbraco-cms/backoffice/utils';
|
|
16
16
|
import { UmbId } from '@umbraco-cms/backoffice/id';
|
|
17
17
|
import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api';
|
|
18
18
|
import { UmbTemporaryFileRepository } from '@umbraco-cms/backoffice/temporary-file';
|
|
@@ -62,8 +62,14 @@ class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase {
|
|
|
62
62
|
tooltip: localize.term('general_mediaPicker'),
|
|
63
63
|
onAction: () => __classPrivateFieldGet(this, _UmbTinyMceMediaPickerPlugin_instances, "m", _UmbTinyMceMediaPickerPlugin_onAction).call(this),
|
|
64
64
|
onSetup: (api) => {
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
// Drive the active state from the SAME resolution the action uses. A selector of
|
|
66
|
+
// `img[data-udi]` only lights up when the image itself is selected, so with the caret in
|
|
67
|
+
// the figcaption the button looked like "nothing selected" while the action would happily
|
|
68
|
+
// have edited that image. Button state and action behaviour must agree.
|
|
69
|
+
const syncActiveState = () => api.setActive(!!__classPrivateFieldGet(this, _UmbTinyMceMediaPickerPlugin_instances, "m", _UmbTinyMceMediaPickerPlugin_resolveExistingMedia).call(this).image);
|
|
70
|
+
this.editor.on('NodeChange', syncActiveState);
|
|
71
|
+
syncActiveState();
|
|
72
|
+
return () => this.editor.off('NodeChange', syncActiveState);
|
|
67
73
|
},
|
|
68
74
|
});
|
|
69
75
|
// Register global options for the editor
|
|
@@ -86,37 +92,51 @@ class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase {
|
|
|
86
92
|
}
|
|
87
93
|
}
|
|
88
94
|
}
|
|
89
|
-
_UmbTinyMceMediaPickerPlugin_modalManager = new WeakMap(), _UmbTinyMceMediaPickerPlugin_temporaryFileRepository = new WeakMap(), _UmbTinyMceMediaPickerPlugin_uploadImageHandler = new WeakMap(), _UmbTinyMceMediaPickerPlugin_instances = new WeakSet(),
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const
|
|
95
|
+
_UmbTinyMceMediaPickerPlugin_modalManager = new WeakMap(), _UmbTinyMceMediaPickerPlugin_temporaryFileRepository = new WeakMap(), _UmbTinyMceMediaPickerPlugin_uploadImageHandler = new WeakMap(), _UmbTinyMceMediaPickerPlugin_instances = new WeakSet(), _UmbTinyMceMediaPickerPlugin_allowedMediaTypeIds_get = function _UmbTinyMceMediaPickerPlugin_allowedMediaTypeIds_get() {
|
|
96
|
+
return splitStringToArray(this.configuration?.getValueByAlias('allowedMediaTypes'));
|
|
97
|
+
}, _UmbTinyMceMediaPickerPlugin_resolveExistingMedia = function _UmbTinyMceMediaPickerPlugin_resolveExistingMedia() {
|
|
98
|
+
const dom = this.editor.dom;
|
|
99
|
+
const selectedNode = this.editor.selection.getNode();
|
|
100
|
+
const figure = dom.getParent(selectedNode, 'figure');
|
|
101
|
+
const image = selectedNode.nodeName === 'IMG'
|
|
102
|
+
? selectedNode
|
|
103
|
+
: (figure?.querySelector('img') ?? null);
|
|
104
|
+
const anchor = image ? dom.getParent(image, 'a') : null;
|
|
105
|
+
// The `<figcaption>` is what the editor actually sees and edits, so it wins over the
|
|
106
|
+
// `data-caption` attribute, which is only written when the picker last ran and goes stale as
|
|
107
|
+
// soon as someone edits the caption directly in the editor.
|
|
108
|
+
const figcaptionText = figure?.querySelector('figcaption')?.textContent?.trim();
|
|
109
|
+
const caption = figcaptionText || image?.dataset.caption || undefined;
|
|
110
|
+
return { selectedNode, figure, image, anchor, caption };
|
|
111
|
+
}, _UmbTinyMceMediaPickerPlugin_onAction = async function _UmbTinyMceMediaPickerPlugin_onAction() {
|
|
112
|
+
const { image, caption } = __classPrivateFieldGet(this, _UmbTinyMceMediaPickerPlugin_instances, "m", _UmbTinyMceMediaPickerPlugin_resolveExistingMedia).call(this);
|
|
99
113
|
let currentTarget = {};
|
|
100
|
-
if (
|
|
101
|
-
const img = selectedElm;
|
|
102
|
-
const hasUdi = img.hasAttribute('data-udi');
|
|
103
|
-
const hasDataTmpImg = img.hasAttribute('data-tmpimg');
|
|
114
|
+
if (image) {
|
|
104
115
|
currentTarget = {
|
|
105
|
-
altText:
|
|
106
|
-
url:
|
|
107
|
-
caption
|
|
116
|
+
altText: image.alt,
|
|
117
|
+
url: image.src,
|
|
118
|
+
caption,
|
|
119
|
+
// Carry the current dimensions in so the dialog opens showing the real size rather
|
|
120
|
+
// than defaulting, and so an unchanged dialog round-trips the size unchanged.
|
|
121
|
+
width: __classPrivateFieldGet(this, _UmbTinyMceMediaPickerPlugin_instances, "m", _UmbTinyMceMediaPickerPlugin_parseDimension).call(this, image.getAttribute('width')),
|
|
122
|
+
height: __classPrivateFieldGet(this, _UmbTinyMceMediaPickerPlugin_instances, "m", _UmbTinyMceMediaPickerPlugin_parseDimension).call(this, image.getAttribute('height')),
|
|
108
123
|
};
|
|
109
|
-
if (
|
|
110
|
-
currentTarget['udi'] =
|
|
124
|
+
if (image.hasAttribute('data-udi')) {
|
|
125
|
+
currentTarget['udi'] = image.dataset.udi;
|
|
111
126
|
}
|
|
112
127
|
else {
|
|
113
|
-
currentTarget['id'] =
|
|
128
|
+
currentTarget['id'] = image.getAttribute('rel') ?? undefined;
|
|
114
129
|
}
|
|
115
|
-
if (
|
|
116
|
-
currentTarget['tmpimg'] =
|
|
130
|
+
if (image.hasAttribute('data-tmpimg')) {
|
|
131
|
+
currentTarget['tmpimg'] = image.dataset.tmpimg;
|
|
117
132
|
}
|
|
118
133
|
}
|
|
119
134
|
__classPrivateFieldGet(this, _UmbTinyMceMediaPickerPlugin_instances, "m", _UmbTinyMceMediaPickerPlugin_showMediaPicker).call(this, currentTarget);
|
|
135
|
+
}, _UmbTinyMceMediaPickerPlugin_parseDimension = function _UmbTinyMceMediaPickerPlugin_parseDimension(value) {
|
|
136
|
+
if (!value)
|
|
137
|
+
return undefined;
|
|
138
|
+
const parsed = Number.parseInt(value, 10);
|
|
139
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
|
|
120
140
|
}, _UmbTinyMceMediaPickerPlugin_showMediaPicker = async function _UmbTinyMceMediaPickerPlugin_showMediaPicker(currentTarget) {
|
|
121
141
|
/*
|
|
122
142
|
let startNodeId;
|
|
@@ -132,10 +152,12 @@ async function _UmbTinyMceMediaPickerPlugin_onAction() {
|
|
|
132
152
|
}
|
|
133
153
|
}
|
|
134
154
|
*/
|
|
155
|
+
const allowedIds = __classPrivateFieldGet(this, _UmbTinyMceMediaPickerPlugin_instances, "a", _UmbTinyMceMediaPickerPlugin_allowedMediaTypeIds_get);
|
|
135
156
|
const modalHandler = __classPrivateFieldGet(this, _UmbTinyMceMediaPickerPlugin_modalManager, "f")?.open(this, UMB_MEDIA_PICKER_MODAL, {
|
|
136
157
|
data: {
|
|
137
158
|
multiple: false,
|
|
138
159
|
//startNodeIsVirtual,
|
|
160
|
+
pickableFilter: allowedIds.length ? (item) => allowedIds.includes(item.mediaType.unique) : undefined,
|
|
139
161
|
},
|
|
140
162
|
value: {
|
|
141
163
|
selection: currentTarget.udi ? [getGuidFromUdi(currentTarget.udi)] : [],
|
|
@@ -152,12 +174,20 @@ async function _UmbTinyMceMediaPickerPlugin_onAction() {
|
|
|
152
174
|
if (!mediaUnique)
|
|
153
175
|
return;
|
|
154
176
|
const modalHandler = __classPrivateFieldGet(this, _UmbTinyMceMediaPickerPlugin_modalManager, "f")?.open(this, UMB_MEDIA_CAPTION_ALT_TEXT_MODAL, {
|
|
155
|
-
|
|
177
|
+
// The dialog needs maxImageSize to offer sensible sizes; without it the size it hands back
|
|
178
|
+
// is unconstrained and we have no idea what the editor's limit was meant to be.
|
|
179
|
+
data: { mediaUnique, maxImageSize: this.editor.options.get('maxImageSize') },
|
|
156
180
|
value: {
|
|
157
181
|
url: '',
|
|
158
182
|
altText: currentTarget.altText,
|
|
159
183
|
caption: currentTarget.caption,
|
|
184
|
+
width: currentTarget.width,
|
|
185
|
+
height: currentTarget.height,
|
|
160
186
|
},
|
|
187
|
+
// The token defaults this sidebar to `small`, which is too narrow for the width/height row —
|
|
188
|
+
// the unit suffix on the height field gets clipped. Umbraco's own tiptap RTE overrides it the
|
|
189
|
+
// same way for the same reason ("Override default sidebar size for better UX").
|
|
190
|
+
modal: { size: 'medium' },
|
|
161
191
|
});
|
|
162
192
|
const mediaData = await modalHandler?.onSubmit().catch(() => null);
|
|
163
193
|
if (!mediaData)
|
|
@@ -167,6 +197,9 @@ async function _UmbTinyMceMediaPickerPlugin_onAction() {
|
|
|
167
197
|
caption: mediaData?.caption,
|
|
168
198
|
url: mediaData?.url,
|
|
169
199
|
udi: 'umb://media/' + mediaUnique?.replace(/-/g, ''),
|
|
200
|
+
// The dialog is the authority on size once the editor has chosen one.
|
|
201
|
+
width: mediaData?.width,
|
|
202
|
+
height: mediaData?.height,
|
|
170
203
|
};
|
|
171
204
|
__classPrivateFieldGet(this, _UmbTinyMceMediaPickerPlugin_instances, "m", _UmbTinyMceMediaPickerPlugin_insertInEditor).call(this, media);
|
|
172
205
|
}, _UmbTinyMceMediaPickerPlugin_insertInEditor = async function _UmbTinyMceMediaPickerPlugin_insertInEditor(media) {
|
|
@@ -174,32 +207,78 @@ async function _UmbTinyMceMediaPickerPlugin_onAction() {
|
|
|
174
207
|
return;
|
|
175
208
|
// We need to create a NEW DOM <img> element to insert
|
|
176
209
|
// setting an attribute of ID to __mcenew, so we can gather a reference to the node, to be able to update its size accordingly to the size of the image.
|
|
210
|
+
// An explicit size from the dialog is authoritative. Stamping it on the markup up front means
|
|
211
|
+
// the editor never briefly renders at the wrong size, and it tells the post-insert sizing step
|
|
212
|
+
// below to leave the dimensions alone.
|
|
213
|
+
const explicitSize = media.width && media.height ? { width: media.width, height: media.height } : undefined;
|
|
177
214
|
const img = {
|
|
178
215
|
alt: media.altText,
|
|
179
216
|
src: media.url ? media.url : 'nothing.jpg',
|
|
180
217
|
id: '__mcenew',
|
|
181
218
|
'data-udi': media.udi,
|
|
182
219
|
'data-caption': media.caption,
|
|
220
|
+
...(explicitSize
|
|
221
|
+
? { width: String(explicitSize.width), height: String(explicitSize.height) }
|
|
222
|
+
: {}),
|
|
183
223
|
};
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
224
|
+
const dom = this.editor.dom;
|
|
225
|
+
// Same resolution the edit path used, so both agree on what is being replaced. The caret may
|
|
226
|
+
// be on the image, on an <a> wrapping it, or inside the figcaption.
|
|
227
|
+
//
|
|
228
|
+
// The anchor matters because Umbraco fixed this same class of bug in its own RTE in 17.6
|
|
229
|
+
// (media-picker.tiptap-toolbar.utils.ts); re-picking used to silently drop the <a>.
|
|
230
|
+
const { selectedNode, figure: existingFigure, image: existingImage, anchor: existingAnchor, } = __classPrivateFieldGet(this, _UmbTinyMceMediaPickerPlugin_instances, "m", _UmbTinyMceMediaPickerPlugin_resolveExistingMedia).call(this);
|
|
231
|
+
let markup = dom.createHTML('img', img);
|
|
232
|
+
if (existingAnchor) {
|
|
233
|
+
// Rebuild the wrapping link. `data-mce-*` attributes are TinyMCE's own bookkeeping and are
|
|
234
|
+
// skipped so they get regenerated rather than staling.
|
|
235
|
+
//
|
|
236
|
+
// Because the new markup now CONTAINS the link, whatever we replace below must be the
|
|
237
|
+
// element the old link lived in — never the image inside it. Replacing just the image would
|
|
238
|
+
// nest an <a> inside an <a>; the parser rejects that and splits it, leaving an empty
|
|
239
|
+
// duplicate link behind.
|
|
240
|
+
const anchorAttrs = {};
|
|
241
|
+
for (const attr of Array.from(existingAnchor.attributes)) {
|
|
242
|
+
if (!attr.name.startsWith('data-mce-')) {
|
|
243
|
+
anchorAttrs[attr.name] = attr.value;
|
|
244
|
+
}
|
|
195
245
|
}
|
|
246
|
+
markup = dom.createHTML('a', anchorAttrs, markup);
|
|
196
247
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
248
|
+
const caption = img['data-caption'];
|
|
249
|
+
if (caption && existingFigure) {
|
|
250
|
+
// Replace only the figure's children, so the figure's own attributes survive along with
|
|
251
|
+
// every sibling elsewhere in the container.
|
|
252
|
+
existingFigure.innerHTML = markup + dom.createHTML('figcaption', {}, caption);
|
|
200
253
|
}
|
|
201
254
|
else {
|
|
202
|
-
|
|
255
|
+
const content = caption ? dom.createHTML('figure', {}, markup + dom.createHTML('figcaption', {}, caption)) : markup;
|
|
256
|
+
// Replace the OUTERMOST element whose markup we have just rebuilt: the figure if there was
|
|
257
|
+
// one, otherwise the link, otherwise the image. Anything narrower would leave a fragment of
|
|
258
|
+
// the old structure behind; anything wider would take out unrelated siblings — the bug in
|
|
259
|
+
// the previous implementation, which assigned to `figure.parentElement.innerHTML`.
|
|
260
|
+
let replaceTarget = existingFigure ?? existingAnchor ?? existingImage;
|
|
261
|
+
if (caption && !existingFigure) {
|
|
262
|
+
// A <figure> is block-level and cannot legally sit inside a <p>. Replacing something
|
|
263
|
+
// *within* the paragraph makes the parser split it, leaving an empty <p> either side of
|
|
264
|
+
// the figure. So when the paragraph holds nothing but this image, promote the target to
|
|
265
|
+
// the paragraph and consume it.
|
|
266
|
+
//
|
|
267
|
+
// Guarded on the paragraph having no text of its own and at most one image, so a figure
|
|
268
|
+
// never swallows a paragraph that has other content in it. `length <= 1` rather than
|
|
269
|
+
// `=== 1` because a brand new insert at a caret has no image in the paragraph yet.
|
|
270
|
+
const paragraph = dom.getParent(replaceTarget ?? selectedNode, 'p');
|
|
271
|
+
if (paragraph && !paragraph.textContent?.trim() && paragraph.querySelectorAll('img').length <= 1) {
|
|
272
|
+
replaceTarget = paragraph;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (replaceTarget) {
|
|
276
|
+
dom.setOuterHTML(replaceTarget, content);
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
// Nothing existing to replace: a brand new insert at the caret.
|
|
280
|
+
this.editor.selection.setContent(content);
|
|
281
|
+
}
|
|
203
282
|
}
|
|
204
283
|
// Using settimeout to wait for a DoM-render, so we can find the new element by ID.
|
|
205
284
|
setTimeout(() => {
|
|
@@ -208,8 +287,13 @@ async function _UmbTinyMceMediaPickerPlugin_onAction() {
|
|
|
208
287
|
return;
|
|
209
288
|
this.editor.dom.setAttrib(imgElm, 'id', null);
|
|
210
289
|
// When image is loaded we are ready to call sizeImageInEditor.
|
|
290
|
+
//
|
|
291
|
+
// `sizeImageInEditor` reads the image's natural size and scales it down to maxImageSize,
|
|
292
|
+
// overwriting the width/height attributes. That is right for pasted or dragged images, but
|
|
293
|
+
// it used to silently discard whatever size the editor chose in the dialog. Passing the
|
|
294
|
+
// explicit size keeps it — the sizer then only refreshes the processed image URL.
|
|
211
295
|
const onImageLoaded = () => {
|
|
212
|
-
sizeImageInEditor(this.editor, imgElm, img.src);
|
|
296
|
+
sizeImageInEditor(this.editor, imgElm, img.src, explicitSize);
|
|
213
297
|
this.editor.dispatch('Change');
|
|
214
298
|
};
|
|
215
299
|
// Check if image already is loaded.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"property-editor-ui-tiny-mce-config-editor.element.d.ts","sourceRoot":"","sources":["../../../src/property-editors/configEditor/property-editor-ui-tiny-mce-config-editor.element.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,KAAK,EACX,0BAA0B,EAC1B,iCAAiC,EACjC,MAAM,yCAAyC,CAAC;AAKjD;;GAEG;AACH,qBACa,6CAA8C,SAAQ,aAAc,YAAW,0BAA0B;;IAErH,IACI,KAAK,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,EAI3B;IACD,IAAI,KAAK,IAAI,IAAI,CAEhB;IAGD,MAAM,CAAC,EAAE,iCAAiC,CAAC;IAG3C,OAAO,CAAC,aAAa,CAA0B;cAEtB,YAAY,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"property-editor-ui-tiny-mce-config-editor.element.d.ts","sourceRoot":"","sources":["../../../src/property-editors/configEditor/property-editor-ui-tiny-mce-config-editor.element.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,KAAK,EACX,0BAA0B,EAC1B,iCAAiC,EACjC,MAAM,yCAAyC,CAAC;AAKjD;;GAEG;AACH,qBACa,6CAA8C,SAAQ,aAAc,YAAW,0BAA0B;;IAErH,IACI,KAAK,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,EAI3B;IACD,IAAI,KAAK,IAAI,IAAI,CAEhB;IAGD,MAAM,CAAC,EAAE,iCAAiC,CAAC;IAG3C,OAAO,CAAC,aAAa,CAA0B;cAEtB,YAAY,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,OAAO,CAAC;IAoC1E,MAAM;IASf,gBAAyB,MAAM,4BAY7B;CACF;AAED,eAAe,6CAA6C,CAAC;AAE7D,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,+CAA+C,EAAE,6CAA6C,CAAC;KAC/F;CACD"}
|
package/dist-cms/property-editors/configEditor/property-editor-ui-tiny-mce-config-editor.element.js
CHANGED
|
@@ -64,7 +64,6 @@ _UmbPropertyEditorUITinyMceConfigEditorElement_showCodeEditor = async function _
|
|
|
64
64
|
else {
|
|
65
65
|
try {
|
|
66
66
|
this._customConfig = JSON.parse(value.content.toString());
|
|
67
|
-
console.log('Parsed object:', this._customConfig);
|
|
68
67
|
// Proceed with using jsonObj
|
|
69
68
|
}
|
|
70
69
|
catch (err) {
|
|
@@ -9,7 +9,7 @@ export const manifests = [
|
|
|
9
9
|
meta: {
|
|
10
10
|
label: 'TinyMCE Plugin Configuration',
|
|
11
11
|
icon: 'icon-autofill',
|
|
12
|
-
group: '
|
|
12
|
+
group: '#propertyEditorUIGroups_common',
|
|
13
13
|
},
|
|
14
14
|
},
|
|
15
15
|
{
|
|
@@ -20,7 +20,7 @@ export const manifests = [
|
|
|
20
20
|
meta: {
|
|
21
21
|
label: 'TinyMCE Toolbar Configuration',
|
|
22
22
|
icon: 'icon-autofill',
|
|
23
|
-
group: '
|
|
23
|
+
group: '#propertyEditorUIGroups_common',
|
|
24
24
|
},
|
|
25
25
|
},
|
|
26
26
|
{
|
|
@@ -31,7 +31,7 @@ export const manifests = [
|
|
|
31
31
|
meta: {
|
|
32
32
|
label: 'TinyMCE Custom Configuration',
|
|
33
33
|
icon: 'icon-autofill',
|
|
34
|
-
group: '
|
|
34
|
+
group: '#propertyEditorUIGroups_common',
|
|
35
35
|
},
|
|
36
36
|
},
|
|
37
37
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"property-editor-ui-tiny-mce-plugin-configuration.element.d.ts","sourceRoot":"","sources":["../../../src/property-editors/plugin/property-editor-ui-tiny-mce-plugin-configuration.element.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AAEpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,KAAK,EACX,0BAA0B,EAC1B,iCAAiC,EACjC,MAAM,yCAAyC,CAAC;AAejD;;GAEG;AACH,qBACa,oDACZ,SAAQ,aACR,YAAW,0BAA0B;;IAErC,IACI,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,EAexC;IACD,IAAI,KAAK,IAAI,MAAM,EAAE,CAEpB;IAGD,MAAM,CAAC,EAAE,iCAAiC,CAAC;IAG3C,OAAO,CAAC,aAAa,CAAsB;cAIlB,YAAY,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,OAAO,CAAC;YAuBrE,UAAU;
|
|
1
|
+
{"version":3,"file":"property-editor-ui-tiny-mce-plugin-configuration.element.d.ts","sourceRoot":"","sources":["../../../src/property-editors/plugin/property-editor-ui-tiny-mce-plugin-configuration.element.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AAEpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,KAAK,EACX,0BAA0B,EAC1B,iCAAiC,EACjC,MAAM,yCAAyC,CAAC;AAejD;;GAEG;AACH,qBACa,oDACZ,SAAQ,aACR,YAAW,0BAA0B;;IAErC,IACI,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,EAexC;IACD,IAAI,KAAK,IAAI,MAAM,EAAE,CAEpB;IAGD,MAAM,CAAC,EAAE,iCAAiC,CAAC;IAG3C,OAAO,CAAC,aAAa,CAAsB;cAIlB,YAAY,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,OAAO,CAAC;YAuBrE,UAAU;IAqGxB,OAAO,CAAC,QAAQ;IAaP,MAAM;IAoBf,gBAAyB,MAAM,4BAS7B;CACF;AAED,eAAe,oDAAoD,CAAC;AAEpE,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,sDAAsD,EAAE,oDAAoD,CAAC;KAC7G;CACD"}
|
package/dist-cms/property-editors/plugin/property-editor-ui-tiny-mce-plugin-configuration.element.js
CHANGED
|
@@ -114,10 +114,20 @@ let UmbPropertyEditorUITinyMcePluginConfigurationElement = class UmbPropertyEdit
|
|
|
114
114
|
});
|
|
115
115
|
});
|
|
116
116
|
}
|
|
117
|
-
|
|
117
|
+
// A plugin can be named by more than one source - the fallback defaults, the appsettings
|
|
118
|
+
// `Plugins` list, and a `tinyMcePlugin` manifest. Each source used to push its own row, so a
|
|
119
|
+
// plugin named twice appeared twice in the picker and handed lit's keyed `repeat()` a duplicate
|
|
120
|
+
// key. First source wins, which keeps the defaults' "(default plugin)" label and disabled state.
|
|
121
|
+
const seen = new Set(this._pluginConfig.map((p) => p.alias));
|
|
122
|
+
const addPlugin = (entry) => {
|
|
123
|
+
if (seen.has(entry.alias))
|
|
124
|
+
return;
|
|
125
|
+
seen.add(entry.alias);
|
|
126
|
+
this._pluginConfig.push(entry);
|
|
127
|
+
};
|
|
118
128
|
configPlugins.forEach((p) => {
|
|
119
|
-
if (!excludeList.includes(p)
|
|
120
|
-
|
|
129
|
+
if (!excludeList.includes(p)) {
|
|
130
|
+
addPlugin({
|
|
121
131
|
alias: p,
|
|
122
132
|
label: p,
|
|
123
133
|
icon: undefined,
|
|
@@ -129,7 +139,7 @@ let UmbPropertyEditorUITinyMcePluginConfigurationElement = class UmbPropertyEdit
|
|
|
129
139
|
plugins.forEach((p) => {
|
|
130
140
|
if (p.meta?.plugins) {
|
|
131
141
|
if (typeof p.meta.plugins === 'string' && !excludeList.includes(p.meta.plugins)) {
|
|
132
|
-
|
|
142
|
+
addPlugin({
|
|
133
143
|
alias: p.meta.plugins,
|
|
134
144
|
label: p.meta.plugins,
|
|
135
145
|
icon: undefined,
|
|
@@ -140,7 +150,7 @@ let UmbPropertyEditorUITinyMcePluginConfigurationElement = class UmbPropertyEdit
|
|
|
140
150
|
else if (Array.isArray(p.meta.plugins)) {
|
|
141
151
|
p.meta.plugins.forEach((pl) => {
|
|
142
152
|
if (!excludeList.includes(pl)) {
|
|
143
|
-
|
|
153
|
+
addPlugin({
|
|
144
154
|
alias: pl,
|
|
145
155
|
label: pl,
|
|
146
156
|
icon: undefined,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifests.d.ts","sourceRoot":"","sources":["../../../src/property-editors/tiny-mce/manifests.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,oBAAoB,
|
|
1
|
+
{"version":3,"file":"manifests.d.ts","sourceRoot":"","sources":["../../../src/property-editors/tiny-mce/manifests.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,oBAAoB,CA0JjD,CAAC"}
|
|
@@ -17,8 +17,9 @@ export const manifests = [
|
|
|
17
17
|
label: 'Rich Text Editor [TinyMCE]',
|
|
18
18
|
propertyEditorSchemaAlias: UMB_BLOCK_RTE_PROPERTY_EDITOR_SCHEMA_ALIAS,
|
|
19
19
|
icon: 'icon-tinymce',
|
|
20
|
-
group: '
|
|
20
|
+
group: '#propertyEditorUIGroups_richContent',
|
|
21
21
|
supportsReadOnly: true,
|
|
22
|
+
keywords: ['content', 'article', 'body', 'html', 'wysiwyg', 'richtext', 'rte', 'tinymce', 'editor', 'formatted', 'paragraph'],
|
|
22
23
|
settings: {
|
|
23
24
|
properties: [
|
|
24
25
|
{
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import type { Editor } from '@umbraco-cms/backoffice/external/tinymce';
|
|
2
2
|
/**
|
|
3
|
-
* Sizes an image in the editor
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* Sizes an image in the editor.
|
|
4
|
+
*
|
|
5
|
+
* By default the image is scaled down to the editor's `maxImageSize`, which is what pasted and
|
|
6
|
+
* dragged images need. Pass `explicitSize` when the size has already been chosen deliberately (for
|
|
7
|
+
* example in the media picker's caption/alt-text dialog) — those dimensions are then used as-is and
|
|
8
|
+
* only the processed image URL is refreshed. Without it, a deliberately chosen size is silently
|
|
9
|
+
* overwritten by the max-size scaling.
|
|
10
|
+
* @param editor The TinyMCE editor instance.
|
|
11
|
+
* @param imageDomElement The image element to size.
|
|
12
|
+
* @param imgUrl Source URL, used to generate a resized `data-mce-src`.
|
|
13
|
+
* @param explicitSize Dimensions to apply verbatim instead of scaling to `maxImageSize`.
|
|
7
14
|
*/
|
|
8
|
-
export declare function sizeImageInEditor(editor: Editor, imageDomElement: HTMLElement, imgUrl?: string
|
|
15
|
+
export declare function sizeImageInEditor(editor: Editor, imageDomElement: HTMLElement, imgUrl?: string, explicitSize?: {
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
}): Promise<void>;
|
|
9
19
|
/**
|
|
10
20
|
* Scales an image to the max size
|
|
11
21
|
* @param maxSize
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0CAA0C,CAAC;AAEvE
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0CAA0C,CAAC;AAEvE;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CACtC,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,WAAW,EAC5B,MAAM,CAAC,EAAE,MAAM,EACf,YAAY,CAAC,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,iBA0ChD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;EA4B5E;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,iBAqDzE;AAED,OAAO,EAAE,wBAAwB,EAAE,MAAM,2CAA2C,CAAC"}
|
package/dist-cms/utils/index.js
CHANGED
|
@@ -3,14 +3,36 @@
|
|
|
3
3
|
// TODO: This should not be done in this way, we need to split this into separate defined helper methods. This is also very specific to TinyMCE, so should be named that way. [NL]
|
|
4
4
|
import { getProcessedImageUrl } from '@umbraco-cms/backoffice/utils';
|
|
5
5
|
/**
|
|
6
|
-
* Sizes an image in the editor
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* Sizes an image in the editor.
|
|
7
|
+
*
|
|
8
|
+
* By default the image is scaled down to the editor's `maxImageSize`, which is what pasted and
|
|
9
|
+
* dragged images need. Pass `explicitSize` when the size has already been chosen deliberately (for
|
|
10
|
+
* example in the media picker's caption/alt-text dialog) — those dimensions are then used as-is and
|
|
11
|
+
* only the processed image URL is refreshed. Without it, a deliberately chosen size is silently
|
|
12
|
+
* overwritten by the max-size scaling.
|
|
13
|
+
* @param editor The TinyMCE editor instance.
|
|
14
|
+
* @param imageDomElement The image element to size.
|
|
15
|
+
* @param imgUrl Source URL, used to generate a resized `data-mce-src`.
|
|
16
|
+
* @param explicitSize Dimensions to apply verbatim instead of scaling to `maxImageSize`.
|
|
10
17
|
*/
|
|
11
|
-
export async function sizeImageInEditor(editor, imageDomElement, imgUrl) {
|
|
18
|
+
export async function sizeImageInEditor(editor, imageDomElement, imgUrl, explicitSize) {
|
|
12
19
|
const size = editor.dom.getSize(imageDomElement);
|
|
13
20
|
const maxImageSize = editor.options.get('maxImageSize');
|
|
21
|
+
if (explicitSize) {
|
|
22
|
+
editor.dom.setAttribs(imageDomElement, {
|
|
23
|
+
width: Math.round(explicitSize.width),
|
|
24
|
+
height: Math.round(explicitSize.height),
|
|
25
|
+
});
|
|
26
|
+
if (imgUrl) {
|
|
27
|
+
const resizedImgUrl = await getProcessedImageUrl(imgUrl, {
|
|
28
|
+
width: Math.round(explicitSize.width),
|
|
29
|
+
height: Math.round(explicitSize.height),
|
|
30
|
+
});
|
|
31
|
+
editor.dom.setAttrib(imageDomElement, 'data-mce-src', resizedImgUrl);
|
|
32
|
+
}
|
|
33
|
+
editor.execCommand('mceAutoResize', false);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
14
36
|
if (maxImageSize && maxImageSize > 0) {
|
|
15
37
|
const newSize = scaleToMaxSize(maxImageSize, size.w, size.h);
|
|
16
38
|
editor.dom.setAttribs(imageDomElement, { width: Math.round(newSize.width), height: Math.round(newSize.height) });
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiny-mce-umbraco/backoffice",
|
|
3
|
-
"version": "17.
|
|
4
|
-
"description": "This Umbraco CMS package enables access to the TinyMCE-based Rich Text Editor (RTE)
|
|
3
|
+
"version": "17.6.2",
|
|
4
|
+
"description": "This Umbraco CMS package enables access to the TinyMCE-based Rich Text Editor (RTE). It requires Umbraco 17.6.2 or later - for Umbraco 16 use the 16.x package versions, for Umbraco 18 use 18.x. It also supports the use of TinyMCE Premium plugins with a valid subscription. Additional features include streamlined configuration for RTE Data Types in Umbraco and enhanced settings that support direct JSON-based configuration via .NET (appsettings.config).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Umbraco",
|
|
7
7
|
"TinyMCE",
|
|
@@ -40,22 +40,22 @@
|
|
|
40
40
|
"@rollup/plugin-json": "^6.1.0",
|
|
41
41
|
"@rollup/plugin-node-resolve": "^16.0.1",
|
|
42
42
|
"@rollup/plugin-replace": "^6.0.2",
|
|
43
|
-
"@umbraco-cms/backoffice": "^17.
|
|
43
|
+
"@umbraco-cms/backoffice": "^17.6.2",
|
|
44
44
|
"chalk": "^5.3.0",
|
|
45
45
|
"cross-env": "^10.1.0",
|
|
46
|
-
"eslint": "^
|
|
46
|
+
"eslint": "^10.2.0",
|
|
47
47
|
"node-fetch": "^3.3.2",
|
|
48
48
|
"rollup": "^4.40.0",
|
|
49
49
|
"rollup-plugin-esbuild": "^6.2.1",
|
|
50
50
|
"rollup-plugin-import-css": "^3.5.8",
|
|
51
51
|
"tiny-glob": "^0.2.9",
|
|
52
52
|
"typescript": "^5.9.3",
|
|
53
|
-
"vite": "^7.
|
|
53
|
+
"vite": "^7.3.2",
|
|
54
54
|
"vite-plugin-static-copy": "^3.1.3",
|
|
55
55
|
"vite-tsconfig-paths": "^5.1.4"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
|
-
"@umbraco-cms/backoffice": "^17.
|
|
58
|
+
"@umbraco-cms/backoffice": "^17.6.2",
|
|
59
59
|
"tinymce": "^6.8.5",
|
|
60
60
|
"tinymce-i18n": "^24.12.30"
|
|
61
61
|
}
|