@vonage/vivid 5.20.0 → 5.21.0
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/alert/index.cjs +1 -1
- package/alert/index.js +1 -1
- package/banner/index.cjs +1 -1
- package/banner/index.js +1 -1
- package/bundled/definition18.cjs +1 -1
- package/bundled/definition18.js +1 -1
- package/bundled/definition6.cjs +1 -1
- package/bundled/definition6.js +1 -1
- package/bundled/vivid-element.cjs +1 -1
- package/bundled/vivid-element.js +1 -1
- package/combobox/index.cjs +1 -1
- package/combobox/index.js +1 -1
- package/custom-elements.json +2635 -2626
- package/file-picker/index.cjs +1 -1
- package/file-picker/index.js +1 -1
- package/lib/rich-text-editor/rte/features/base.d.ts +1 -0
- package/lib/rich-text-editor/rte/features/internal/history.d.ts +6 -0
- package/lib/rich-text-editor/rte/utils/ui.d.ts +2 -0
- package/locales/de-DE.cjs +178 -4
- package/locales/de-DE.js +179 -2
- package/locales/en-GB.cjs +9 -4
- package/locales/en-GB.js +10 -2
- package/locales/en-US.cjs +2 -270
- package/locales/en-US.js +1 -267
- package/locales/ja-JP.cjs +171 -4
- package/locales/ja-JP.js +172 -2
- package/locales/zh-CN.cjs +172 -4
- package/locales/zh-CN.js +173 -2
- package/package.json +6 -5
- package/rich-text-editor/index.cjs +12 -12
- package/rich-text-editor/index.js +935 -916
- package/searchable-select/index.cjs +1 -1
- package/searchable-select/index.js +1 -1
- package/switch/index.cjs +1 -1
- package/switch/index.js +1 -1
- package/tabs/index.cjs +1 -1
- package/tabs/index.js +1 -1
- package/unbundled/chunk.cjs +15 -0
- package/unbundled/chunk.js +13 -0
- package/unbundled/definition17.cjs +1 -1
- package/unbundled/definition17.js +1 -1
- package/unbundled/definition30.cjs +1 -1
- package/unbundled/definition30.js +1 -1
- package/unbundled/definition43.cjs +1 -1
- package/unbundled/definition43.js +1 -1
- package/unbundled/definition60.cjs +801 -767
- package/unbundled/definition60.js +801 -767
- package/unbundled/definition61.cjs +1 -1
- package/unbundled/definition61.js +1 -1
- package/unbundled/definition63.cjs +1 -1
- package/unbundled/definition63.js +1 -1
- package/unbundled/definition69.cjs +1 -1
- package/unbundled/definition69.js +1 -1
- package/unbundled/definition7.cjs +1 -1
- package/unbundled/definition7.js +1 -1
- package/unbundled/definition73.cjs +1 -1
- package/unbundled/definition73.js +1 -1
- package/unbundled/definition9.cjs +1 -1
- package/unbundled/definition9.js +1 -1
- package/unbundled/en-US.cjs +449 -0
- package/unbundled/en-US.js +445 -0
- package/unbundled/localized.cjs +2 -2
- package/unbundled/localized.js +1 -1
- package/unbundled/vivid-element.cjs +1 -1
- package/unbundled/vivid-element.js +1 -1
|
@@ -23,9 +23,9 @@ let prosemirror_keymap = require("prosemirror-keymap");
|
|
|
23
23
|
let prosemirror_commands = require("prosemirror-commands");
|
|
24
24
|
let prosemirror_dropcursor = require("prosemirror-dropcursor");
|
|
25
25
|
let prosemirror_gapcursor = require("prosemirror-gapcursor");
|
|
26
|
-
let prosemirror_history = require("prosemirror-history");
|
|
27
26
|
let dompurify = require("dompurify");
|
|
28
27
|
dompurify = require_chunk.__toESM(dompurify, 1);
|
|
28
|
+
let prosemirror_history = require("prosemirror-history");
|
|
29
29
|
let prosemirror_inputrules = require("prosemirror-inputrules");
|
|
30
30
|
let prosemirror_transform = require("prosemirror-transform");
|
|
31
31
|
let prosemirror_schema_basic = require("prosemirror-schema-basic");
|
|
@@ -240,645 +240,333 @@ var FeatureState = class {
|
|
|
240
240
|
//#region src/lib/rich-text-editor/rte/features/internal/core.style.scss?inline
|
|
241
241
|
var core_style_default = ".ProseMirror{box-sizing:border-box;padding:var(--editor-padding-block) var(--editor-padding-inline);outline:none;flex:1 0 0}.ProseMirror-selectednode{--focus-stroke-gap-color:transparent;--focus-border-radius:2px;outline:none;position:relative}.ProseMirror-selectednode:after{box-shadow:0 0 0 4px color-mix(in srgb, var(--focus-stroke-color,var(--vvd-color-cta-500)), transparent 85%), inset 0 0 0 3px var(--focus-stroke-gap-color,currentColor);outline:1px solid var(--focus-stroke-color,var(--vvd-color-cta-500));outline-offset:calc(-1px - var(--focus-inset,0px));border-radius:var(--focus-border-radius,inherit);block-size:calc(100% + var(--focus-block-size-addition,4px));content:\"\";inline-size:calc(100% + var(--focus-block-size-addition,4px));display:block;position:absolute;inset-block-start:50%;inset-inline-start:50%;transform:translate(-50%,-50%)}.editor--disabled .ProseMirror{color:var(--vvd-color-neutral-300);cursor:not-allowed}";
|
|
242
242
|
//#endregion
|
|
243
|
-
//#region src/lib/rich-text-editor/rte/
|
|
244
|
-
var
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
handler
|
|
249
|
-
];
|
|
250
|
-
var UiCtx = class {
|
|
251
|
-
constructor(view, rte, props) {
|
|
252
|
-
this.view = view;
|
|
253
|
-
this.rte = rte;
|
|
254
|
-
this.props = props;
|
|
255
|
-
this.bindings = [];
|
|
256
|
-
}
|
|
257
|
-
evalProp(prop) {
|
|
258
|
-
return isPropBinding(prop) ? prop(this) : prop;
|
|
243
|
+
//#region src/lib/rich-text-editor/rte/features/internal/foreign-html.ts
|
|
244
|
+
var RteForeignHtmlFeatureImpl = class extends require_slottable_request.RteFeatureImpl {
|
|
245
|
+
constructor(..._args) {
|
|
246
|
+
super(..._args);
|
|
247
|
+
this.name = "RteForeignHtmlFeature";
|
|
259
248
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
} else bindFn(prop);
|
|
249
|
+
getPlugins(rte) {
|
|
250
|
+
return [this.contribution(new prosemirror_state.Plugin({ props: {
|
|
251
|
+
transformPastedHTML: (html) => rte.foreignHtmlParser[require_slottable_request.impl].transform(html),
|
|
252
|
+
clipboardParser: rte.foreignHtmlParser[require_slottable_request.impl].parser,
|
|
253
|
+
clipboardSerializer: rte.foreignHtmlSerializer[require_slottable_request.impl].serializer
|
|
254
|
+
} }))];
|
|
267
255
|
}
|
|
268
|
-
|
|
269
|
-
|
|
256
|
+
};
|
|
257
|
+
//#endregion
|
|
258
|
+
//#region src/lib/rich-text-editor/rte/features/internal/cursor-fix.ts
|
|
259
|
+
/**
|
|
260
|
+
* When the cursor is positioned after an inline atom node at the end of a line,
|
|
261
|
+
* browsers may display the cursor inside the atom.
|
|
262
|
+
*/
|
|
263
|
+
var atomCursorFix = ($cursor) => {
|
|
264
|
+
if (!($cursor.parentOffset === $cursor.parent.content.size)) return null;
|
|
265
|
+
const nodeBefore = $cursor.nodeBefore;
|
|
266
|
+
if (nodeBefore && nodeBefore.isInline && nodeBefore.isAtom && !nodeBefore.isText) return {};
|
|
267
|
+
return null;
|
|
268
|
+
};
|
|
269
|
+
var RteCursorFixFeatureImpl = class extends require_slottable_request.RteFeatureImpl {
|
|
270
|
+
constructor(..._args) {
|
|
271
|
+
super(..._args);
|
|
272
|
+
this.name = "RteCursorFix";
|
|
270
273
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
274
|
+
getPlugins(rte) {
|
|
275
|
+
const cursorFixes = [atomCursorFix];
|
|
276
|
+
for (const markType of Object.values(rte.schema.marks)) {
|
|
277
|
+
const spec = markType.spec;
|
|
278
|
+
if (spec.cursorFix) cursorFixes.push(spec.cursorFix);
|
|
279
|
+
}
|
|
280
|
+
return [this.contribution(new prosemirror_state.Plugin({ props: { decorations: (state) => {
|
|
281
|
+
const { $cursor } = state.selection;
|
|
282
|
+
if (!$cursor) return null;
|
|
283
|
+
let cursorFix = null;
|
|
284
|
+
for (const fn of cursorFixes) {
|
|
285
|
+
const result = fn($cursor, state);
|
|
286
|
+
if (result) Object.assign(cursorFix ??= {}, result);
|
|
287
|
+
}
|
|
288
|
+
if (!cursorFix) return null;
|
|
289
|
+
return prosemirror_view.DecorationSet.create(state.doc, [prosemirror_view.Decoration.widget($cursor.pos, () => {
|
|
290
|
+
const span = document.createElement("span");
|
|
291
|
+
span.textContent = "";
|
|
292
|
+
for (const [prop, value] of Object.entries(cursorFix)) span.style.setProperty(prop, value);
|
|
293
|
+
return span;
|
|
294
|
+
}, { side: -1 })]);
|
|
295
|
+
} } }))];
|
|
277
296
|
}
|
|
278
297
|
};
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
298
|
+
require_slottable_request.featureFacade(RteCursorFixFeatureImpl);
|
|
299
|
+
//#endregion
|
|
300
|
+
//#region src/lib/rich-text-editor/rte/features/internal/core.ts
|
|
301
|
+
/**
|
|
302
|
+
* Plugin to bring state from the host web component into ProseMirror.
|
|
303
|
+
*/
|
|
304
|
+
var hostBridgePlugin = new prosemirror_state.Plugin({ state: {
|
|
305
|
+
init() {
|
|
306
|
+
return null;
|
|
307
|
+
},
|
|
308
|
+
apply(tr, value) {
|
|
309
|
+
const meta = tr.getMeta(hostBridgePlugin);
|
|
310
|
+
if (meta) return meta;
|
|
311
|
+
else return value;
|
|
312
|
+
}
|
|
313
|
+
} });
|
|
314
|
+
var RteCoreImpl = class extends require_slottable_request.RteFeatureImpl {
|
|
315
|
+
constructor(..._args) {
|
|
316
|
+
super(..._args);
|
|
317
|
+
this.name = "RteCore";
|
|
318
|
+
this.disabled = new FeatureState(false);
|
|
319
|
+
}
|
|
320
|
+
getStyles() {
|
|
321
|
+
return [
|
|
322
|
+
this.contribution(prosemirror_default),
|
|
323
|
+
this.contribution(core_style_default),
|
|
324
|
+
this.contribution(ui_style_default)
|
|
325
|
+
];
|
|
326
|
+
}
|
|
327
|
+
getPlugins(rte) {
|
|
328
|
+
const enterKeyChainCommands = (0, prosemirror_commands.chainCommands)(prosemirror_commands.newlineInCode, prosemirror_commands.createParagraphNear, prosemirror_commands.liftEmptyBlock, (0, prosemirror_commands.splitBlockAs)((node, atEnd, $from) => {
|
|
329
|
+
if (!atEnd) return {
|
|
330
|
+
type: node.type,
|
|
331
|
+
attrs: node.attrs
|
|
332
|
+
};
|
|
333
|
+
return {
|
|
334
|
+
type: defaultTextblockForMatch($from.node($from.depth - 1).contentMatchAt($from.indexAfter($from.depth - 1))),
|
|
335
|
+
attrs: rte.textblockAttrs.extractFromNode(node)
|
|
336
|
+
};
|
|
337
|
+
}));
|
|
338
|
+
return [
|
|
339
|
+
this.contribution(this.disabled.plugin),
|
|
340
|
+
this.contribution(new prosemirror_state.Plugin({
|
|
341
|
+
props: {
|
|
342
|
+
editable: () => !this.disabled.getValue(rte),
|
|
343
|
+
handleDOMEvents: { click: (_view, event) => {
|
|
344
|
+
if (this.disabled.getValue(rte)) {
|
|
345
|
+
event.preventDefault();
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
return false;
|
|
349
|
+
} }
|
|
350
|
+
},
|
|
351
|
+
view: (view) => {
|
|
352
|
+
const popovers = view.dom.getRootNode().querySelector(".popovers");
|
|
353
|
+
const updateDisabled = () => {
|
|
354
|
+
const disabled = this.disabled.getValue(rte);
|
|
355
|
+
popovers.classList.toggle("popovers--disabled", disabled);
|
|
356
|
+
view.dom.parentElement.classList.toggle("editor--disabled", disabled);
|
|
357
|
+
};
|
|
358
|
+
updateDisabled();
|
|
359
|
+
return { update: () => {
|
|
360
|
+
updateDisabled();
|
|
361
|
+
} };
|
|
362
|
+
}
|
|
363
|
+
})),
|
|
364
|
+
this.contribution((0, prosemirror_keymap.keymap)({
|
|
365
|
+
...prosemirror_commands.baseKeymap,
|
|
366
|
+
Enter: enterKeyChainCommands,
|
|
367
|
+
"Shift-Enter": enterKeyChainCommands
|
|
368
|
+
})),
|
|
369
|
+
this.contribution((0, prosemirror_dropcursor.dropCursor)()),
|
|
370
|
+
this.contribution((0, prosemirror_gapcursor.gapCursor)()),
|
|
371
|
+
this.contribution(hostBridgePlugin)
|
|
372
|
+
];
|
|
373
|
+
}
|
|
374
|
+
getFeatures() {
|
|
375
|
+
return [
|
|
376
|
+
this,
|
|
377
|
+
new RteForeignHtmlFeatureImpl(),
|
|
378
|
+
new RteCursorFixFeatureImpl()
|
|
379
|
+
];
|
|
380
|
+
}
|
|
286
381
|
};
|
|
287
|
-
|
|
382
|
+
require_slottable_request.featureFacade(RteCoreImpl);
|
|
383
|
+
//#endregion
|
|
384
|
+
//#region src/lib/rich-text-editor/rte/utils/sanitization.ts
|
|
385
|
+
var DEFAULT_ALLOWED_URI_REGEXP = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
|
|
386
|
+
var ALLOWED_URI_REGEXP_WITH_BLOB = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|blob):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
|
|
387
|
+
var ATTR_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
|
|
388
|
+
var domPurifyConfig = { ALLOWED_URI_REGEXP: ALLOWED_URI_REGEXP_WITH_BLOB };
|
|
389
|
+
/**
|
|
390
|
+
* Sanitize potentially dangerous URLs, like "javascript:", in anchor href attributes.
|
|
391
|
+
* Returns empty string if the URL is unsafe.
|
|
392
|
+
*/
|
|
393
|
+
var sanitizeLinkHref = (url) => {
|
|
394
|
+
if (!DEFAULT_ALLOWED_URI_REGEXP.test(url.replace(ATTR_WHITESPACE, ""))) return "";
|
|
288
395
|
const anchor = document.createElement("a");
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
295
|
-
return anchor;
|
|
396
|
+
anchor.setAttribute("href", url);
|
|
397
|
+
/* v8 ignore next -- since href is already validated it's probably always present @preserve */
|
|
398
|
+
return dompurify.default.sanitize(anchor, {
|
|
399
|
+
RETURN_DOM: true,
|
|
400
|
+
...domPurifyConfig
|
|
401
|
+
}).querySelector("a").getAttribute("href") ?? "";
|
|
296
402
|
};
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
tooltip.anchor = enabled ? props.anchor : void 0;
|
|
309
|
-
});
|
|
310
|
-
const wrapper = createDiv(ctx, {
|
|
311
|
-
className: "ui-tooltip-wrapper",
|
|
312
|
-
slot: props.slot,
|
|
313
|
-
children: [props.anchor, tooltip]
|
|
314
|
-
});
|
|
315
|
-
wrapperTargets.set(wrapper, props.anchor);
|
|
316
|
-
return wrapper;
|
|
403
|
+
/**
|
|
404
|
+
* Sanitize potentially dangerous URLs, like "javascript:", in image src attributes.
|
|
405
|
+
* Returns empty string if the URL is unsafe.
|
|
406
|
+
*/
|
|
407
|
+
var sanitizeImageSrc = (url) => {
|
|
408
|
+
const img = document.createElement("img");
|
|
409
|
+
img.setAttribute("src", url);
|
|
410
|
+
return dompurify.default.sanitize(img, {
|
|
411
|
+
RETURN_DOM: true,
|
|
412
|
+
...domPurifyConfig
|
|
413
|
+
}).querySelector("img").getAttribute("src") ?? "";
|
|
317
414
|
};
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
appearance,
|
|
330
|
-
disabled,
|
|
331
|
-
icon: props.icon,
|
|
332
|
-
autofocus: props.autofocus,
|
|
333
|
-
connotation,
|
|
334
|
-
[props.icon ? "ariaLabel" : "label"]: props.label,
|
|
335
|
-
pressed: () => Boolean(ctx.evalProp(props.active)),
|
|
336
|
-
ariaPressed: props.active !== void 0 ? () => ctx.evalProp(props.active) ? "true" : "false" : void 0
|
|
337
|
-
}, [on("click", props.onClick, (onClick) => () => {
|
|
338
|
-
if (!onClick()) ctx.view.focus();
|
|
339
|
-
})]);
|
|
340
|
-
return createOptionalTooltip(ctx, {
|
|
341
|
-
enabled: () => Boolean(ctx.evalProp(props.icon) && !ctx.evalProp(props.noTooltip) && !disabled()),
|
|
342
|
-
label: props.label,
|
|
343
|
-
anchor: button,
|
|
344
|
-
slot: props.slot
|
|
345
|
-
});
|
|
346
|
-
};
|
|
347
|
-
var createMenu = (ctx, props) => {
|
|
348
|
-
const menu = ctx.rte.createComponent(require_definition$4.Menu);
|
|
349
|
-
ctx.bindToEl(menu, {
|
|
350
|
-
className: "ui-menu",
|
|
351
|
-
autoDismiss: true,
|
|
352
|
-
ariaLabel: props.label,
|
|
353
|
-
anchor: wrapperTargets.get(props.trigger) ?? props.trigger,
|
|
354
|
-
placement: ctx.props.popupPlacement,
|
|
355
|
-
offset: () => ctx.evalProp(ctx.props.menuOffset) ?? null
|
|
356
|
-
}, [], props.children);
|
|
357
|
-
const fragment = document.createDocumentFragment();
|
|
358
|
-
fragment.appendChild(props.trigger);
|
|
359
|
-
fragment.appendChild(menu);
|
|
360
|
-
return fragment;
|
|
361
|
-
};
|
|
362
|
-
var createMenuItem = (ctx, props) => {
|
|
363
|
-
const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled) || ctx.evalProp(props.disabled));
|
|
364
|
-
const item = ctx.rte.createComponent(require_definition$3.MenuItem);
|
|
365
|
-
ctx.bindToEl(item, {
|
|
366
|
-
text: props.text,
|
|
367
|
-
checked: props.checked,
|
|
368
|
-
disabled,
|
|
369
|
-
controlType: "radio",
|
|
370
|
-
checkedAppearance: "tick-only"
|
|
371
|
-
}, [on("change", props.onSelect, (onSelect) => () => {
|
|
372
|
-
if (item.checked && !item.disabled) {
|
|
373
|
-
if (ctx.evalProp(props.checked) !== item.checked) {
|
|
374
|
-
onSelect();
|
|
375
|
-
ctx.view.focus();
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
})]);
|
|
379
|
-
return item;
|
|
380
|
-
};
|
|
381
|
-
var createButtonGroup = (ctx, props) => createDiv(ctx, {
|
|
382
|
-
slot: props.slot,
|
|
383
|
-
className: "ui-button-group",
|
|
384
|
-
children: props.children
|
|
415
|
+
/**
|
|
416
|
+
* Escapes a CSS value for insertion into style attribute.
|
|
417
|
+
* E.g. "15px; background: red" => "15px"
|
|
418
|
+
*/
|
|
419
|
+
var escapeCssProperty = (value) => value.replace(/[;!].*/, "");
|
|
420
|
+
//#endregion
|
|
421
|
+
//#region src/lib/rich-text-editor/rte/html-parser.ts
|
|
422
|
+
var copy = (obj) => ({ ...obj });
|
|
423
|
+
var parseRulesFromSchema = (schema) => ({
|
|
424
|
+
nodes: Object.fromEntries(Object.keys(schema.nodes).map((name) => [name, (schema.nodes[name].spec.parseDOM ?? []).map(copy)])),
|
|
425
|
+
marks: Object.fromEntries(Object.keys(schema.marks).map((name) => [name, (schema.marks[name].spec.parseDOM ?? []).map(copy)]))
|
|
385
426
|
});
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
427
|
+
var RteHtmlParser = class {
|
|
428
|
+
constructor(config, options) {
|
|
429
|
+
this[require_slottable_request.impl] = new RteHtmlParserImpl(config[require_slottable_request.impl], options);
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Converts an HTML string to an RteDocument.
|
|
433
|
+
*/
|
|
434
|
+
parseDocument(html, options) {
|
|
435
|
+
return this[require_slottable_request.impl].parser.parse(this.parseHtml(html, options), { preserveWhitespace: true }).toJSON();
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Converts an HTML string to an RteFragment.
|
|
439
|
+
*/
|
|
440
|
+
parseFragment(html, options) {
|
|
441
|
+
return this[require_slottable_request.impl].parser.parseSlice(this.parseHtml(html, options), { preserveWhitespace: true }).content.toJSON() ?? [];
|
|
442
|
+
}
|
|
443
|
+
parseHtml(html, options) {
|
|
444
|
+
const dom = dompurify.default.sanitize(html, {
|
|
445
|
+
RETURN_DOM: true,
|
|
446
|
+
...domPurifyConfig
|
|
447
|
+
});
|
|
448
|
+
const container = document.createDocumentFragment();
|
|
449
|
+
container.appendChild(dom);
|
|
450
|
+
options?.modifyDom?.(container);
|
|
451
|
+
return container;
|
|
398
452
|
}
|
|
399
|
-
});
|
|
400
|
-
var createOption = (ctx, props) => {
|
|
401
|
-
const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled) || ctx.evalProp(props.disabled));
|
|
402
|
-
const option = ctx.rte.createComponent(require_definition$7.ListboxOption);
|
|
403
|
-
ctx.bindToEl(option, {
|
|
404
|
-
value: props.value,
|
|
405
|
-
text: props.text,
|
|
406
|
-
disabled
|
|
407
|
-
});
|
|
408
|
-
return option;
|
|
409
453
|
};
|
|
410
|
-
var
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
454
|
+
var RteHtmlParserImpl = class {
|
|
455
|
+
constructor(config, options) {
|
|
456
|
+
const rules = parseRulesFromSchema(config.schema);
|
|
457
|
+
options?.modifyParseRules?.(rules);
|
|
458
|
+
this.parser = buildDomParser(config.schema, rules);
|
|
459
|
+
}
|
|
460
|
+
transform(html) {
|
|
461
|
+
return dompurify.default.sanitize(html);
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
var buildDomParser = (schema, { marks, nodes }) => {
|
|
465
|
+
const parserRules = [];
|
|
466
|
+
const priority = (rule) => rule.priority ?? 50;
|
|
467
|
+
function insert(rule) {
|
|
468
|
+
let i = 0;
|
|
469
|
+
for (; i < parserRules.length; i++) {
|
|
470
|
+
const next = parserRules[i];
|
|
471
|
+
if (priority(next) < priority(rule)) break;
|
|
424
472
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
select.addEventListener("vwc-popup:open", () => {
|
|
431
|
-
hideTooltip = true;
|
|
432
|
-
ctx.updateBindings();
|
|
433
|
-
});
|
|
434
|
-
select.addEventListener("vwc-popup:close", () => {
|
|
435
|
-
hideTooltip = false;
|
|
436
|
-
ctx.updateBindings();
|
|
473
|
+
parserRules.splice(i, 0, rule);
|
|
474
|
+
}
|
|
475
|
+
for (const name in marks) marks[name]?.forEach((rule) => {
|
|
476
|
+
insert(rule = copy(rule));
|
|
477
|
+
if (!(rule.mark || rule.ignore || rule.clearMark)) rule.mark = name;
|
|
437
478
|
});
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
anchor: select
|
|
479
|
+
for (const name in nodes) nodes[name]?.forEach((rule) => {
|
|
480
|
+
insert(rule = copy(rule));
|
|
481
|
+
if (!(rule.node || rule.ignore || rule.mark)) rule.node = name;
|
|
442
482
|
});
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
ctx.bindToEl(divider, {
|
|
447
|
-
className: "ui-divider",
|
|
448
|
-
orientation: "vertical"
|
|
483
|
+
parserRules.push({
|
|
484
|
+
tag: "br",
|
|
485
|
+
closeParent: true
|
|
449
486
|
});
|
|
450
|
-
return
|
|
451
|
-
};
|
|
452
|
-
var createTextField = (ctx, props) => {
|
|
453
|
-
const textField = ctx.rte.createComponent(require_definition$5.TextField);
|
|
454
|
-
ctx.bindToEl(textField, {
|
|
455
|
-
label: props.label,
|
|
456
|
-
value: props.value,
|
|
457
|
-
placeholder: props.placeholder,
|
|
458
|
-
slot: props.slot,
|
|
459
|
-
autofocus: props.autofocus,
|
|
460
|
-
type: props.type,
|
|
461
|
-
helperText: props.helperText
|
|
462
|
-
}, [on("input", props.onInput, (onInput) => () => {
|
|
463
|
-
onInput(textField.value);
|
|
464
|
-
})]);
|
|
465
|
-
return textField;
|
|
466
|
-
};
|
|
467
|
-
var createText = (ctx, props) => {
|
|
468
|
-
const textNode = document.createTextNode("");
|
|
469
|
-
ctx.bindToEl(textNode, { textContent: props.text });
|
|
470
|
-
return textNode;
|
|
487
|
+
return new prosemirror_model.DOMParser(schema, parserRules);
|
|
471
488
|
};
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
function cleanup() {
|
|
478
|
-
for (const { el, type, handler } of listeners) el.removeEventListener(type, handler);
|
|
479
|
-
listeners.length = 0;
|
|
480
|
-
currentEl = null;
|
|
489
|
+
//#endregion
|
|
490
|
+
//#region src/lib/rich-text-editor/rte/html-serializer.ts
|
|
491
|
+
var RteHtmlSerializer = class {
|
|
492
|
+
constructor(config, options) {
|
|
493
|
+
this[require_slottable_request.impl] = new RteHtmlSerializerImpl(config[require_slottable_request.impl], options);
|
|
481
494
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
listeners.push({
|
|
494
|
-
el,
|
|
495
|
-
type,
|
|
496
|
-
handler: listener
|
|
497
|
-
});
|
|
498
|
-
}
|
|
495
|
+
/**
|
|
496
|
+
* Converts an RteDocument to an HTML string.
|
|
497
|
+
*/
|
|
498
|
+
serializeDocument(doc, options) {
|
|
499
|
+
return this[require_slottable_request.impl].serializeFragment(doc.content, options);
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Converts an RteFragment to an HTML string.
|
|
503
|
+
*/
|
|
504
|
+
serializeFragment(fragment, options) {
|
|
505
|
+
return this[require_slottable_request.impl].serializeFragment(fragment, options);
|
|
499
506
|
}
|
|
500
|
-
const processSlot = () => {
|
|
501
|
-
const first = slot.assignedElements({ flatten: true })[0];
|
|
502
|
-
if (first === currentEl) return;
|
|
503
|
-
cleanup();
|
|
504
|
-
if (first) {
|
|
505
|
-
currentEl = first;
|
|
506
|
-
applyPropsAndEvents(first);
|
|
507
|
-
}
|
|
508
|
-
};
|
|
509
|
-
slot.addEventListener("slotchange", processSlot);
|
|
510
|
-
queueMicrotask(processSlot);
|
|
511
|
-
return slot;
|
|
512
507
|
};
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
508
|
+
var RteHtmlSerializerImpl = class RteHtmlSerializerImpl {
|
|
509
|
+
constructor(config, options) {
|
|
510
|
+
this.config = config;
|
|
511
|
+
const serializers = RteHtmlSerializerImpl.domSerializersFromSchema(config.schema);
|
|
512
|
+
Object.assign(serializers.nodes, options?.serializers?.nodes ?? {});
|
|
513
|
+
Object.assign(serializers.marks, options?.serializers?.marks ?? {});
|
|
514
|
+
this.serializer = new prosemirror_model.DOMSerializer(serializers.nodes, serializers.marks);
|
|
519
515
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
return [this.contribution({
|
|
529
|
-
section: "history",
|
|
530
|
-
render: (ctx) => createButton(ctx, {
|
|
531
|
-
label: (ctx) => ctx.rte.getLocale().richTextEditor.undo,
|
|
532
|
-
icon: "undo-line",
|
|
533
|
-
disabled: (ctx) => !(0, prosemirror_history.undo)(ctx.view.state),
|
|
534
|
-
onClick: () => {
|
|
535
|
-
const { state, dispatch } = ctx.view;
|
|
536
|
-
(0, prosemirror_history.undo)(state, dispatch);
|
|
537
|
-
}
|
|
538
|
-
})
|
|
539
|
-
}, 1), this.contribution({
|
|
540
|
-
section: "history",
|
|
541
|
-
render: (ctx) => createButton(ctx, {
|
|
542
|
-
label: (ctx) => ctx.rte.getLocale().richTextEditor.redo,
|
|
543
|
-
icon: "redo-line",
|
|
544
|
-
disabled: (ctx) => !(0, prosemirror_history.redo)(ctx.view.state),
|
|
545
|
-
onClick: () => {
|
|
546
|
-
const { state, dispatch } = ctx.view;
|
|
547
|
-
(0, prosemirror_history.redo)(state, dispatch);
|
|
548
|
-
}
|
|
549
|
-
})
|
|
550
|
-
}, 2)];
|
|
551
|
-
}
|
|
552
|
-
};
|
|
553
|
-
//#endregion
|
|
554
|
-
//#region src/lib/rich-text-editor/rte/features/internal/foreign-html.ts
|
|
555
|
-
var RteForeignHtmlFeatureImpl = class extends require_slottable_request.RteFeatureImpl {
|
|
556
|
-
constructor(..._args) {
|
|
557
|
-
super(..._args);
|
|
558
|
-
this.name = "RteForeignHtmlFeature";
|
|
559
|
-
}
|
|
560
|
-
getPlugins(rte) {
|
|
561
|
-
return [this.contribution(new prosemirror_state.Plugin({ props: {
|
|
562
|
-
transformPastedHTML: (html) => rte.foreignHtmlParser[require_slottable_request.impl].transform(html),
|
|
563
|
-
clipboardParser: rte.foreignHtmlParser[require_slottable_request.impl].parser,
|
|
564
|
-
clipboardSerializer: rte.foreignHtmlSerializer[require_slottable_request.impl].serializer
|
|
565
|
-
} }))];
|
|
566
|
-
}
|
|
567
|
-
};
|
|
568
|
-
//#endregion
|
|
569
|
-
//#region src/lib/rich-text-editor/rte/features/internal/cursor-fix.ts
|
|
570
|
-
/**
|
|
571
|
-
* When the cursor is positioned after an inline atom node at the end of a line,
|
|
572
|
-
* browsers may display the cursor inside the atom.
|
|
573
|
-
*/
|
|
574
|
-
var atomCursorFix = ($cursor) => {
|
|
575
|
-
if (!($cursor.parentOffset === $cursor.parent.content.size)) return null;
|
|
576
|
-
const nodeBefore = $cursor.nodeBefore;
|
|
577
|
-
if (nodeBefore && nodeBefore.isInline && nodeBefore.isAtom && !nodeBefore.isText) return {};
|
|
578
|
-
return null;
|
|
579
|
-
};
|
|
580
|
-
var RteCursorFixFeatureImpl = class extends require_slottable_request.RteFeatureImpl {
|
|
581
|
-
constructor(..._args) {
|
|
582
|
-
super(..._args);
|
|
583
|
-
this.name = "RteCursorFix";
|
|
584
|
-
}
|
|
585
|
-
getPlugins(rte) {
|
|
586
|
-
const cursorFixes = [atomCursorFix];
|
|
587
|
-
for (const markType of Object.values(rte.schema.marks)) {
|
|
588
|
-
const spec = markType.spec;
|
|
589
|
-
if (spec.cursorFix) cursorFixes.push(spec.cursorFix);
|
|
516
|
+
static domSerializersFromSchema(schema) {
|
|
517
|
+
const result = {
|
|
518
|
+
nodes: {},
|
|
519
|
+
marks: {}
|
|
520
|
+
};
|
|
521
|
+
for (const name in schema.marks) {
|
|
522
|
+
const toDOM = schema.marks[name].spec.toDOM;
|
|
523
|
+
if (toDOM) result.marks[name] = toDOM;
|
|
590
524
|
}
|
|
591
|
-
|
|
592
|
-
const
|
|
593
|
-
if (
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
if (result) Object.assign(cursorFix ??= {}, result);
|
|
598
|
-
}
|
|
599
|
-
if (!cursorFix) return null;
|
|
600
|
-
return prosemirror_view.DecorationSet.create(state.doc, [prosemirror_view.Decoration.widget($cursor.pos, () => {
|
|
601
|
-
const span = document.createElement("span");
|
|
602
|
-
span.textContent = "";
|
|
603
|
-
for (const [prop, value] of Object.entries(cursorFix)) span.style.setProperty(prop, value);
|
|
604
|
-
return span;
|
|
605
|
-
}, { side: -1 })]);
|
|
606
|
-
} } }))];
|
|
607
|
-
}
|
|
608
|
-
};
|
|
609
|
-
require_slottable_request.featureFacade(RteCursorFixFeatureImpl);
|
|
610
|
-
//#endregion
|
|
611
|
-
//#region src/lib/rich-text-editor/rte/features/internal/core.ts
|
|
612
|
-
/**
|
|
613
|
-
* Plugin to bring state from the host web component into ProseMirror.
|
|
614
|
-
*/
|
|
615
|
-
var hostBridgePlugin = new prosemirror_state.Plugin({ state: {
|
|
616
|
-
init() {
|
|
617
|
-
return null;
|
|
618
|
-
},
|
|
619
|
-
apply(tr, value) {
|
|
620
|
-
const meta = tr.getMeta(hostBridgePlugin);
|
|
621
|
-
if (meta) return meta;
|
|
622
|
-
else return value;
|
|
623
|
-
}
|
|
624
|
-
} });
|
|
625
|
-
var RteCoreImpl = class extends require_slottable_request.RteFeatureImpl {
|
|
626
|
-
constructor(..._args) {
|
|
627
|
-
super(..._args);
|
|
628
|
-
this.name = "RteCore";
|
|
629
|
-
this.disabled = new FeatureState(false);
|
|
630
|
-
}
|
|
631
|
-
getStyles() {
|
|
632
|
-
return [
|
|
633
|
-
this.contribution(prosemirror_default),
|
|
634
|
-
this.contribution(core_style_default),
|
|
635
|
-
this.contribution(ui_style_default)
|
|
636
|
-
];
|
|
637
|
-
}
|
|
638
|
-
getPlugins(rte) {
|
|
639
|
-
const enterKeyChainCommands = (0, prosemirror_commands.chainCommands)(prosemirror_commands.newlineInCode, prosemirror_commands.createParagraphNear, prosemirror_commands.liftEmptyBlock, (0, prosemirror_commands.splitBlockAs)((node, atEnd, $from) => {
|
|
640
|
-
if (!atEnd) return {
|
|
641
|
-
type: node.type,
|
|
642
|
-
attrs: node.attrs
|
|
643
|
-
};
|
|
644
|
-
return {
|
|
645
|
-
type: defaultTextblockForMatch($from.node($from.depth - 1).contentMatchAt($from.indexAfter($from.depth - 1))),
|
|
646
|
-
attrs: rte.textblockAttrs.extractFromNode(node)
|
|
647
|
-
};
|
|
648
|
-
}));
|
|
649
|
-
return [
|
|
650
|
-
this.contribution(this.disabled.plugin),
|
|
651
|
-
this.contribution(new prosemirror_state.Plugin({
|
|
652
|
-
props: {
|
|
653
|
-
editable: () => !this.disabled.getValue(rte),
|
|
654
|
-
handleDOMEvents: { click: (_view, event) => {
|
|
655
|
-
if (this.disabled.getValue(rte)) {
|
|
656
|
-
event.preventDefault();
|
|
657
|
-
return true;
|
|
658
|
-
}
|
|
659
|
-
return false;
|
|
660
|
-
} }
|
|
661
|
-
},
|
|
662
|
-
view: (view) => {
|
|
663
|
-
const popovers = view.dom.getRootNode().querySelector(".popovers");
|
|
664
|
-
const updateDisabled = () => {
|
|
665
|
-
const disabled = this.disabled.getValue(rte);
|
|
666
|
-
popovers.classList.toggle("popovers--disabled", disabled);
|
|
667
|
-
view.dom.parentElement.classList.toggle("editor--disabled", disabled);
|
|
668
|
-
};
|
|
669
|
-
updateDisabled();
|
|
670
|
-
return { update: () => {
|
|
671
|
-
updateDisabled();
|
|
672
|
-
} };
|
|
673
|
-
}
|
|
674
|
-
})),
|
|
675
|
-
this.contribution((0, prosemirror_keymap.keymap)({
|
|
676
|
-
...prosemirror_commands.baseKeymap,
|
|
677
|
-
Enter: enterKeyChainCommands,
|
|
678
|
-
"Shift-Enter": enterKeyChainCommands
|
|
679
|
-
})),
|
|
680
|
-
this.contribution((0, prosemirror_dropcursor.dropCursor)()),
|
|
681
|
-
this.contribution((0, prosemirror_gapcursor.gapCursor)()),
|
|
682
|
-
this.contribution(hostBridgePlugin)
|
|
683
|
-
];
|
|
525
|
+
for (const name in schema.nodes) {
|
|
526
|
+
const toDOM = schema.nodes[name].spec.serializeToDOM ?? schema.nodes[name].spec.toDOM;
|
|
527
|
+
if (toDOM) result.nodes[name] = toDOM;
|
|
528
|
+
}
|
|
529
|
+
result.nodes.text = (node) => document.createTextNode(node.text);
|
|
530
|
+
return result;
|
|
684
531
|
}
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
532
|
+
serializeFragment(fragment, options) {
|
|
533
|
+
const parsedFragment = prosemirror_model.Fragment.fromJSON(this.config.schema, fragment);
|
|
534
|
+
const serializedFragment = this.serializer.serializeFragment(parsedFragment);
|
|
535
|
+
const container = document.createDocumentFragment();
|
|
536
|
+
container.appendChild(serializedFragment);
|
|
537
|
+
options?.modifyDom?.(container);
|
|
538
|
+
const output = document.createElement("div");
|
|
539
|
+
output.appendChild(container);
|
|
540
|
+
return output.innerHTML;
|
|
692
541
|
}
|
|
693
542
|
};
|
|
694
|
-
require_slottable_request.featureFacade(RteCoreImpl);
|
|
695
543
|
//#endregion
|
|
696
|
-
//#region src/lib/rich-text-editor/rte/
|
|
697
|
-
var
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
* Sanitize potentially dangerous URLs, like "javascript:", in anchor href attributes.
|
|
703
|
-
* Returns empty string if the URL is unsafe.
|
|
704
|
-
*/
|
|
705
|
-
var sanitizeLinkHref = (url) => {
|
|
706
|
-
if (!DEFAULT_ALLOWED_URI_REGEXP.test(url.replace(ATTR_WHITESPACE, ""))) return "";
|
|
707
|
-
const anchor = document.createElement("a");
|
|
708
|
-
anchor.setAttribute("href", url);
|
|
709
|
-
/* v8 ignore next -- since href is already validated it's probably always present @preserve */
|
|
710
|
-
return dompurify.default.sanitize(anchor, {
|
|
711
|
-
RETURN_DOM: true,
|
|
712
|
-
...domPurifyConfig
|
|
713
|
-
}).querySelector("a").getAttribute("href") ?? "";
|
|
714
|
-
};
|
|
715
|
-
/**
|
|
716
|
-
* Sanitize potentially dangerous URLs, like "javascript:", in image src attributes.
|
|
717
|
-
* Returns empty string if the URL is unsafe.
|
|
718
|
-
*/
|
|
719
|
-
var sanitizeImageSrc = (url) => {
|
|
720
|
-
const img = document.createElement("img");
|
|
721
|
-
img.setAttribute("src", url);
|
|
722
|
-
return dompurify.default.sanitize(img, {
|
|
723
|
-
RETURN_DOM: true,
|
|
724
|
-
...domPurifyConfig
|
|
725
|
-
}).querySelector("img").getAttribute("src") ?? "";
|
|
544
|
+
//#region src/lib/rich-text-editor/rte/instance.ts
|
|
545
|
+
var parseDocument = (schema, doc) => {
|
|
546
|
+
const node = schema.topNodeType.createAndFill(null, doc ? prosemirror_model.Fragment.fromJSON(schema, doc.content) : null);
|
|
547
|
+
if (!node) throw new Error("Document could not be parsed");
|
|
548
|
+
node.check();
|
|
549
|
+
return node;
|
|
726
550
|
};
|
|
727
|
-
|
|
728
|
-
* Escapes a CSS value for insertion into style attribute.
|
|
729
|
-
* E.g. "15px; background: red" => "15px"
|
|
730
|
-
*/
|
|
731
|
-
var escapeCssProperty = (value) => value.replace(/[;!].*/, "");
|
|
732
|
-
//#endregion
|
|
733
|
-
//#region src/lib/rich-text-editor/rte/html-parser.ts
|
|
734
|
-
var copy = (obj) => ({ ...obj });
|
|
735
|
-
var parseRulesFromSchema = (schema) => ({
|
|
736
|
-
nodes: Object.fromEntries(Object.keys(schema.nodes).map((name) => [name, (schema.nodes[name].spec.parseDOM ?? []).map(copy)])),
|
|
737
|
-
marks: Object.fromEntries(Object.keys(schema.marks).map((name) => [name, (schema.marks[name].spec.parseDOM ?? []).map(copy)]))
|
|
738
|
-
});
|
|
739
|
-
var RteHtmlParser = class {
|
|
551
|
+
var RteInstance = class {
|
|
740
552
|
constructor(config, options) {
|
|
741
|
-
this
|
|
553
|
+
this.options = options;
|
|
554
|
+
this.feature = (Feature, featureId) => {
|
|
555
|
+
return this[require_slottable_request.impl].getPublicInterface(Feature, featureId);
|
|
556
|
+
};
|
|
557
|
+
this[require_slottable_request.impl] = new RteInstanceImpl(this, config, options);
|
|
742
558
|
}
|
|
743
559
|
/**
|
|
744
|
-
*
|
|
560
|
+
* Returns the current document state.
|
|
745
561
|
*/
|
|
746
|
-
|
|
747
|
-
return this[require_slottable_request.impl].
|
|
562
|
+
getDocument() {
|
|
563
|
+
return this[require_slottable_request.impl].state.doc.toJSON();
|
|
748
564
|
}
|
|
749
565
|
/**
|
|
750
|
-
*
|
|
566
|
+
* Reset the editor to its initial state. Optionally, an initial document can be provided.
|
|
751
567
|
*/
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
}
|
|
755
|
-
parseHtml(html, options) {
|
|
756
|
-
const dom = dompurify.default.sanitize(html, {
|
|
757
|
-
RETURN_DOM: true,
|
|
758
|
-
...domPurifyConfig
|
|
759
|
-
});
|
|
760
|
-
const container = document.createDocumentFragment();
|
|
761
|
-
container.appendChild(dom);
|
|
762
|
-
options?.modifyDom?.(container);
|
|
763
|
-
return container;
|
|
764
|
-
}
|
|
765
|
-
};
|
|
766
|
-
var RteHtmlParserImpl = class {
|
|
767
|
-
constructor(config, options) {
|
|
768
|
-
const rules = parseRulesFromSchema(config.schema);
|
|
769
|
-
options?.modifyParseRules?.(rules);
|
|
770
|
-
this.parser = buildDomParser(config.schema, rules);
|
|
771
|
-
}
|
|
772
|
-
transform(html) {
|
|
773
|
-
return dompurify.default.sanitize(html);
|
|
774
|
-
}
|
|
775
|
-
};
|
|
776
|
-
var buildDomParser = (schema, { marks, nodes }) => {
|
|
777
|
-
const parserRules = [];
|
|
778
|
-
const priority = (rule) => rule.priority ?? 50;
|
|
779
|
-
function insert(rule) {
|
|
780
|
-
let i = 0;
|
|
781
|
-
for (; i < parserRules.length; i++) {
|
|
782
|
-
const next = parserRules[i];
|
|
783
|
-
if (priority(next) < priority(rule)) break;
|
|
784
|
-
}
|
|
785
|
-
parserRules.splice(i, 0, rule);
|
|
786
|
-
}
|
|
787
|
-
for (const name in marks) marks[name]?.forEach((rule) => {
|
|
788
|
-
insert(rule = copy(rule));
|
|
789
|
-
if (!(rule.mark || rule.ignore || rule.clearMark)) rule.mark = name;
|
|
790
|
-
});
|
|
791
|
-
for (const name in nodes) nodes[name]?.forEach((rule) => {
|
|
792
|
-
insert(rule = copy(rule));
|
|
793
|
-
if (!(rule.node || rule.ignore || rule.mark)) rule.node = name;
|
|
794
|
-
});
|
|
795
|
-
parserRules.push({
|
|
796
|
-
tag: "br",
|
|
797
|
-
closeParent: true
|
|
798
|
-
});
|
|
799
|
-
return new prosemirror_model.DOMParser(schema, parserRules);
|
|
800
|
-
};
|
|
801
|
-
//#endregion
|
|
802
|
-
//#region src/lib/rich-text-editor/rte/html-serializer.ts
|
|
803
|
-
var RteHtmlSerializer = class {
|
|
804
|
-
constructor(config, options) {
|
|
805
|
-
this[require_slottable_request.impl] = new RteHtmlSerializerImpl(config[require_slottable_request.impl], options);
|
|
806
|
-
}
|
|
807
|
-
/**
|
|
808
|
-
* Converts an RteDocument to an HTML string.
|
|
809
|
-
*/
|
|
810
|
-
serializeDocument(doc, options) {
|
|
811
|
-
return this[require_slottable_request.impl].serializeFragment(doc.content, options);
|
|
812
|
-
}
|
|
813
|
-
/**
|
|
814
|
-
* Converts an RteFragment to an HTML string.
|
|
815
|
-
*/
|
|
816
|
-
serializeFragment(fragment, options) {
|
|
817
|
-
return this[require_slottable_request.impl].serializeFragment(fragment, options);
|
|
818
|
-
}
|
|
819
|
-
};
|
|
820
|
-
var RteHtmlSerializerImpl = class RteHtmlSerializerImpl {
|
|
821
|
-
constructor(config, options) {
|
|
822
|
-
this.config = config;
|
|
823
|
-
const serializers = RteHtmlSerializerImpl.domSerializersFromSchema(config.schema);
|
|
824
|
-
Object.assign(serializers.nodes, options?.serializers?.nodes ?? {});
|
|
825
|
-
Object.assign(serializers.marks, options?.serializers?.marks ?? {});
|
|
826
|
-
this.serializer = new prosemirror_model.DOMSerializer(serializers.nodes, serializers.marks);
|
|
827
|
-
}
|
|
828
|
-
static domSerializersFromSchema(schema) {
|
|
829
|
-
const result = {
|
|
830
|
-
nodes: {},
|
|
831
|
-
marks: {}
|
|
832
|
-
};
|
|
833
|
-
for (const name in schema.marks) {
|
|
834
|
-
const toDOM = schema.marks[name].spec.toDOM;
|
|
835
|
-
if (toDOM) result.marks[name] = toDOM;
|
|
836
|
-
}
|
|
837
|
-
for (const name in schema.nodes) {
|
|
838
|
-
const toDOM = schema.nodes[name].spec.serializeToDOM ?? schema.nodes[name].spec.toDOM;
|
|
839
|
-
if (toDOM) result.nodes[name] = toDOM;
|
|
840
|
-
}
|
|
841
|
-
result.nodes.text = (node) => document.createTextNode(node.text);
|
|
842
|
-
return result;
|
|
843
|
-
}
|
|
844
|
-
serializeFragment(fragment, options) {
|
|
845
|
-
const parsedFragment = prosemirror_model.Fragment.fromJSON(this.config.schema, fragment);
|
|
846
|
-
const serializedFragment = this.serializer.serializeFragment(parsedFragment);
|
|
847
|
-
const container = document.createDocumentFragment();
|
|
848
|
-
container.appendChild(serializedFragment);
|
|
849
|
-
options?.modifyDom?.(container);
|
|
850
|
-
const output = document.createElement("div");
|
|
851
|
-
output.appendChild(container);
|
|
852
|
-
return output.innerHTML;
|
|
853
|
-
}
|
|
854
|
-
};
|
|
855
|
-
//#endregion
|
|
856
|
-
//#region src/lib/rich-text-editor/rte/instance.ts
|
|
857
|
-
var parseDocument = (schema, doc) => {
|
|
858
|
-
const node = schema.topNodeType.createAndFill(null, doc ? prosemirror_model.Fragment.fromJSON(schema, doc.content) : null);
|
|
859
|
-
if (!node) throw new Error("Document could not be parsed");
|
|
860
|
-
node.check();
|
|
861
|
-
return node;
|
|
862
|
-
};
|
|
863
|
-
var RteInstance = class {
|
|
864
|
-
constructor(config, options) {
|
|
865
|
-
this.options = options;
|
|
866
|
-
this.feature = (Feature, featureId) => {
|
|
867
|
-
return this[require_slottable_request.impl].getPublicInterface(Feature, featureId);
|
|
868
|
-
};
|
|
869
|
-
this[require_slottable_request.impl] = new RteInstanceImpl(this, config, options);
|
|
870
|
-
}
|
|
871
|
-
/**
|
|
872
|
-
* Returns the current document state.
|
|
873
|
-
*/
|
|
874
|
-
getDocument() {
|
|
875
|
-
return this[require_slottable_request.impl].state.doc.toJSON();
|
|
876
|
-
}
|
|
877
|
-
/**
|
|
878
|
-
* Reset the editor to its initial state. Optionally, an initial document can be provided.
|
|
879
|
-
*/
|
|
880
|
-
reset(initialDocument) {
|
|
881
|
-
this[require_slottable_request.impl].reset(initialDocument);
|
|
568
|
+
reset(initialDocument) {
|
|
569
|
+
this[require_slottable_request.impl].reset(initialDocument);
|
|
882
570
|
}
|
|
883
571
|
/**
|
|
884
572
|
* Replaces the current selection with the given content.
|
|
@@ -993,171 +681,487 @@ var RteInstanceImpl = class {
|
|
|
993
681
|
this.dispatchTransaction(this.tr.setMeta(hostBridgePlugin, hostState));
|
|
994
682
|
}
|
|
995
683
|
};
|
|
996
|
-
//#endregion
|
|
997
|
-
//#region src/lib/rich-text-editor/rte/utils/textblock-attrs.ts
|
|
998
|
-
var TextblockAttrs = class {
|
|
999
|
-
constructor(specs) {
|
|
1000
|
-
this.specs = specs;
|
|
1001
|
-
this.attrs = {};
|
|
1002
|
-
for (const spec of specs) this.attrs[spec.name] = { default: spec.default };
|
|
1003
|
-
}
|
|
1004
|
-
fromDOM(dom) {
|
|
1005
|
-
return Object.assign({}, ...this.specs.map((s) => ({ [s.name]: s.fromDOM(dom) })));
|
|
1006
|
-
}
|
|
1007
|
-
getStyle(node) {
|
|
1008
|
-
return this.specs.flatMap((s) => s.toStyles(node)).join("; ");
|
|
1009
|
-
}
|
|
1010
|
-
getDOMAttrsProperties(node) {
|
|
1011
|
-
if (!this.specs.length) return {};
|
|
1012
|
-
return { style: this.getStyle(node) };
|
|
1013
|
-
}
|
|
1014
|
-
getDOMAttrs(node) {
|
|
1015
|
-
if (!this.specs.length) return [];
|
|
1016
|
-
return [this.getDOMAttrsProperties(node)];
|
|
1017
|
-
}
|
|
1018
|
-
extractFromNode(node) {
|
|
1019
|
-
return Object.assign({}, ...Object.keys(this.attrs).map((attr) => ({ [attr]: node.attrs[attr] })));
|
|
1020
|
-
}
|
|
684
|
+
//#endregion
|
|
685
|
+
//#region src/lib/rich-text-editor/rte/utils/textblock-attrs.ts
|
|
686
|
+
var TextblockAttrs = class {
|
|
687
|
+
constructor(specs) {
|
|
688
|
+
this.specs = specs;
|
|
689
|
+
this.attrs = {};
|
|
690
|
+
for (const spec of specs) this.attrs[spec.name] = { default: spec.default };
|
|
691
|
+
}
|
|
692
|
+
fromDOM(dom) {
|
|
693
|
+
return Object.assign({}, ...this.specs.map((s) => ({ [s.name]: s.fromDOM(dom) })));
|
|
694
|
+
}
|
|
695
|
+
getStyle(node) {
|
|
696
|
+
return this.specs.flatMap((s) => s.toStyles(node)).join("; ");
|
|
697
|
+
}
|
|
698
|
+
getDOMAttrsProperties(node) {
|
|
699
|
+
if (!this.specs.length) return {};
|
|
700
|
+
return { style: this.getStyle(node) };
|
|
701
|
+
}
|
|
702
|
+
getDOMAttrs(node) {
|
|
703
|
+
if (!this.specs.length) return [];
|
|
704
|
+
return [this.getDOMAttrsProperties(node)];
|
|
705
|
+
}
|
|
706
|
+
extractFromNode(node) {
|
|
707
|
+
return Object.assign({}, ...Object.keys(this.attrs).map((attr) => ({ [attr]: node.attrs[attr] })));
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
//#endregion
|
|
711
|
+
//#region src/lib/rich-text-editor/rte/utils/textblock-marks.ts
|
|
712
|
+
var TextblockMarks = class {
|
|
713
|
+
constructor(specs) {
|
|
714
|
+
this.specs = specs;
|
|
715
|
+
}
|
|
716
|
+
getAllowedMarksForNode(nodeName) {
|
|
717
|
+
return this.specs.filter((spec) => !spec.onNodeName || spec.onNodeName === nodeName).map((spec) => spec.markName);
|
|
718
|
+
}
|
|
719
|
+
getReferencedNodeNames() {
|
|
720
|
+
return new Set(this.specs.map((spec) => spec.onNodeName).filter(Boolean));
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
//#endregion
|
|
724
|
+
//#region src/lib/rich-text-editor/rte/view.ts
|
|
725
|
+
/**
|
|
726
|
+
* Converts an RteDocument to an RteView tree for rendering outside the editor.
|
|
727
|
+
* The logic is adapted from ProseMirror's DOM serialization to ensure the resulting structure
|
|
728
|
+
* matches the editor's HTML structure.
|
|
729
|
+
*/
|
|
730
|
+
var convertToView = (doc, ctx) => {
|
|
731
|
+
const schema = ctx.config[require_slottable_request.impl].schema;
|
|
732
|
+
const getMarkRank = (mark) => schema.marks[mark.type].rank;
|
|
733
|
+
const marksEqual = (a, b) => {
|
|
734
|
+
if (a.type !== b.type) return false;
|
|
735
|
+
const attrsA = a.attrs;
|
|
736
|
+
const attrsB = b.attrs;
|
|
737
|
+
if (attrsA === attrsB) return true;
|
|
738
|
+
/* v8 ignore next -- not currently reachable since all attrs are required @preserve */
|
|
739
|
+
if (!attrsA || !attrsB) return false;
|
|
740
|
+
const keysA = Object.keys(attrsA);
|
|
741
|
+
const keysB = Object.keys(attrsB);
|
|
742
|
+
/* v8 ignore next -- not currently reachable since all attrs are required @preserve */
|
|
743
|
+
if (keysA.length !== keysB.length) return false;
|
|
744
|
+
for (const key of keysA) if (attrsA[key] !== attrsB[key]) return false;
|
|
745
|
+
return true;
|
|
746
|
+
};
|
|
747
|
+
const convertFragment = (nodes) => {
|
|
748
|
+
const result = [];
|
|
749
|
+
const active = [];
|
|
750
|
+
const activeContents = [result];
|
|
751
|
+
for (const node of nodes) {
|
|
752
|
+
const nodeMarks = [...node.marks ?? []].sort((a, b) => getMarkRank(a) - getMarkRank(b));
|
|
753
|
+
let keep = 0;
|
|
754
|
+
let rendered = 0;
|
|
755
|
+
while (keep < active.length && rendered < nodeMarks.length) {
|
|
756
|
+
if (!marksEqual(active[keep], nodeMarks[rendered])) break;
|
|
757
|
+
keep++;
|
|
758
|
+
rendered++;
|
|
759
|
+
}
|
|
760
|
+
while (active.length > keep) {
|
|
761
|
+
active.pop();
|
|
762
|
+
activeContents.pop();
|
|
763
|
+
}
|
|
764
|
+
while (rendered < nodeMarks.length) {
|
|
765
|
+
const mark = nodeMarks[rendered];
|
|
766
|
+
const markContent = [];
|
|
767
|
+
activeContents[activeContents.length - 1].push({
|
|
768
|
+
type: "mark",
|
|
769
|
+
mark,
|
|
770
|
+
children: {
|
|
771
|
+
type: "fragment",
|
|
772
|
+
content: markContent,
|
|
773
|
+
[require_slottable_request.impl]: ctx
|
|
774
|
+
},
|
|
775
|
+
[require_slottable_request.impl]: ctx
|
|
776
|
+
});
|
|
777
|
+
active.push(mark);
|
|
778
|
+
activeContents.push(markContent);
|
|
779
|
+
rendered++;
|
|
780
|
+
}
|
|
781
|
+
activeContents[activeContents.length - 1].push({
|
|
782
|
+
type: "node",
|
|
783
|
+
node,
|
|
784
|
+
children: convertFragment(node.content ?? []),
|
|
785
|
+
[require_slottable_request.impl]: ctx
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
return {
|
|
789
|
+
type: "fragment",
|
|
790
|
+
content: result,
|
|
791
|
+
[require_slottable_request.impl]: ctx
|
|
792
|
+
};
|
|
793
|
+
};
|
|
794
|
+
return {
|
|
795
|
+
type: "node",
|
|
796
|
+
node: doc,
|
|
797
|
+
children: convertFragment(doc.content),
|
|
798
|
+
[require_slottable_request.impl]: ctx
|
|
799
|
+
};
|
|
800
|
+
};
|
|
801
|
+
//#endregion
|
|
802
|
+
//#region src/lib/rich-text-editor/rte/config.ts
|
|
803
|
+
var RteConfig = class {
|
|
804
|
+
constructor(features) {
|
|
805
|
+
this[require_slottable_request.impl] = new RteConfigImpl(features);
|
|
806
|
+
}
|
|
807
|
+
instantiateEditor(options) {
|
|
808
|
+
return new RteInstance(this, options);
|
|
809
|
+
}
|
|
810
|
+
instantiateView(document, options) {
|
|
811
|
+
return convertToView(document, {
|
|
812
|
+
config: this,
|
|
813
|
+
options: { ...options }
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
var RteConfigImpl = class {
|
|
818
|
+
constructor(featuresFacades) {
|
|
819
|
+
const resolveFeatures = (features) => features.flatMap((f) => f.getFeatures().flatMap((subFeature) => subFeature === f ? f : resolveFeatures([subFeature])));
|
|
820
|
+
this.features = resolveFeatures(featuresFacades.map(require_slottable_request.getFeatureImpl));
|
|
821
|
+
this.featureMap = /* @__PURE__ */ new Map();
|
|
822
|
+
for (const f of this.features) {
|
|
823
|
+
if (this.featureMap.has(f.name)) throw new Error(`Duplicate feature: ${f.name}`);
|
|
824
|
+
this.featureMap.set(f.name, f);
|
|
825
|
+
}
|
|
826
|
+
this.featureFacadesMap = /* @__PURE__ */ new Map();
|
|
827
|
+
for (const facade of featuresFacades) {
|
|
828
|
+
const FacadeClass = facade.constructor;
|
|
829
|
+
const feature = require_slottable_request.getFeatureImpl(facade);
|
|
830
|
+
const instances = this.featureFacadesMap.get(FacadeClass);
|
|
831
|
+
if (instances) instances.push(feature);
|
|
832
|
+
else this.featureFacadesMap.set(FacadeClass, [feature]);
|
|
833
|
+
}
|
|
834
|
+
if (!this.featureMap.has("RteBase")) throw new Error("RteBase feature is required");
|
|
835
|
+
if (this.featureMap.has("RteLinkFeature") && !this.featureMap.has("RteToolbarFeature")) throw new Error("RteToolbarFeature is required for RteLinkFeature");
|
|
836
|
+
this.textblockAttrs = new TextblockAttrs(require_slottable_request.sortedContributions(this.features.flatMap((f) => f.getTextblockAttrs())));
|
|
837
|
+
this.textblockMarks = new TextblockMarks(require_slottable_request.sortedContributions(this.features.flatMap((f) => f.getTextblockMarks())));
|
|
838
|
+
const schemaContributions = require_slottable_request.sortedContributions(this.features.flatMap((f) => f.getSchema(this.textblockAttrs, this.textblockMarks)));
|
|
839
|
+
const schemaSpec = {
|
|
840
|
+
nodes: {},
|
|
841
|
+
marks: {}
|
|
842
|
+
};
|
|
843
|
+
for (const schema of schemaContributions) {
|
|
844
|
+
Object.assign(schemaSpec.nodes, schema.nodes ?? {});
|
|
845
|
+
Object.assign(schemaSpec.marks, schema.marks ?? {});
|
|
846
|
+
}
|
|
847
|
+
for (const referencedNodeName of this.textblockMarks.getReferencedNodeNames()) if (!(referencedNodeName in schemaSpec.nodes)) throw new Error(`Unknown node "${referencedNodeName}"`);
|
|
848
|
+
this.schema = new prosemirror_model.Schema(schemaSpec);
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
//#endregion
|
|
852
|
+
//#region src/lib/rich-text-editor/rte/utils/ui.ts
|
|
853
|
+
var isPropBinding = (prop) => typeof prop === "function";
|
|
854
|
+
var on = (event, prop, handler) => [
|
|
855
|
+
event,
|
|
856
|
+
prop,
|
|
857
|
+
handler
|
|
858
|
+
];
|
|
859
|
+
var UiCtx = class {
|
|
860
|
+
constructor(view, rte, props) {
|
|
861
|
+
this.view = view;
|
|
862
|
+
this.rte = rte;
|
|
863
|
+
this.props = props;
|
|
864
|
+
this.bindings = [];
|
|
865
|
+
}
|
|
866
|
+
evalProp(prop) {
|
|
867
|
+
return isPropBinding(prop) ? prop(this) : prop;
|
|
868
|
+
}
|
|
869
|
+
bindProp(prop, bindFn) {
|
|
870
|
+
if (prop === void 0) return;
|
|
871
|
+
else if (isPropBinding(prop)) {
|
|
872
|
+
const binding = () => bindFn(prop(this));
|
|
873
|
+
this.bindings.push(binding);
|
|
874
|
+
binding();
|
|
875
|
+
} else bindFn(prop);
|
|
876
|
+
}
|
|
877
|
+
updateBindings() {
|
|
878
|
+
for (const binding of this.bindings) binding();
|
|
879
|
+
}
|
|
880
|
+
shouldReturnFocusToEditor() {
|
|
881
|
+
return this.evalProp(this.props.shouldReturnFocusToEditor);
|
|
882
|
+
}
|
|
883
|
+
bindToEl(target, props = {}, events = [], children = []) {
|
|
884
|
+
for (const name in props) this.bindProp(props[name], (value) => {
|
|
885
|
+
target[name] = value;
|
|
886
|
+
});
|
|
887
|
+
for (const [name, value, bindFn] of events) if (value) target.addEventListener(name, bindFn(value));
|
|
888
|
+
for (const child of children) target.appendChild(child);
|
|
889
|
+
}
|
|
890
|
+
};
|
|
891
|
+
var createDiv = (ctx, props) => {
|
|
892
|
+
const div = document.createElement("div");
|
|
893
|
+
ctx.bindToEl(div, {
|
|
894
|
+
className: props.className,
|
|
895
|
+
slot: props.slot
|
|
896
|
+
}, [], props.children);
|
|
897
|
+
return div;
|
|
898
|
+
};
|
|
899
|
+
var createAnchor = (ctx, props) => {
|
|
900
|
+
const anchor = document.createElement("a");
|
|
901
|
+
ctx.bindToEl(anchor, {
|
|
902
|
+
href: props.href,
|
|
903
|
+
target: props.target,
|
|
904
|
+
rel: props.rel,
|
|
905
|
+
className: props.className
|
|
906
|
+
}, [], props.children);
|
|
907
|
+
return anchor;
|
|
908
|
+
};
|
|
909
|
+
var wrapperTargets = /* @__PURE__ */ new WeakMap();
|
|
910
|
+
var createOptionalTooltip = (ctx, props) => {
|
|
911
|
+
const tooltip = ctx.rte.createComponent(require_definition$6.Tooltip);
|
|
912
|
+
tooltip.setAttribute("exportparts", "vvd-theme-alternate");
|
|
913
|
+
ctx.bindToEl(tooltip, {
|
|
914
|
+
className: "ui-tooltip",
|
|
915
|
+
text: props.label,
|
|
916
|
+
placement: ctx.props.popupPlacement
|
|
917
|
+
});
|
|
918
|
+
ctx.bindProp(props.enabled, (enabled) => {
|
|
919
|
+
if (!enabled) tooltip.open = false;
|
|
920
|
+
tooltip.anchor = enabled ? props.anchor : void 0;
|
|
921
|
+
});
|
|
922
|
+
const wrapper = createDiv(ctx, {
|
|
923
|
+
className: "ui-tooltip-wrapper",
|
|
924
|
+
slot: props.slot,
|
|
925
|
+
children: [props.anchor, tooltip]
|
|
926
|
+
});
|
|
927
|
+
wrapperTargets.set(wrapper, props.anchor);
|
|
928
|
+
return wrapper;
|
|
929
|
+
};
|
|
930
|
+
var createButton = (ctx, props) => {
|
|
931
|
+
const variant = () => ctx.evalProp(props.variant) ?? "toolbar";
|
|
932
|
+
const size = () => variant() === "toolbar" ? "super-condensed" : "condensed";
|
|
933
|
+
const appearanceInactive = () => variant() === "popover-primary" ? "outlined" : "ghost-light";
|
|
934
|
+
const appearanceActive = () => variant() === "toolbar-menu" || variant() === "popover" ? "filled" : appearanceInactive();
|
|
935
|
+
const appearance = () => ctx.evalProp(props.active) ? appearanceActive() : appearanceInactive();
|
|
936
|
+
const connotation = () => ctx.evalProp(props.connotation) ?? (variant() === "toolbar-menu" && ctx.evalProp(props.active) ? "cta" : void 0);
|
|
937
|
+
const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled) || ctx.evalProp(props.disabled));
|
|
938
|
+
const button = ctx.rte.createComponent(require_definition$1.Button);
|
|
939
|
+
ctx.bindToEl(button, {
|
|
940
|
+
size,
|
|
941
|
+
appearance,
|
|
942
|
+
disabled,
|
|
943
|
+
icon: props.icon,
|
|
944
|
+
autofocus: props.autofocus,
|
|
945
|
+
connotation,
|
|
946
|
+
[props.icon ? "ariaLabel" : "label"]: props.label,
|
|
947
|
+
pressed: () => Boolean(ctx.evalProp(props.active)),
|
|
948
|
+
ariaPressed: props.active !== void 0 ? () => ctx.evalProp(props.active) ? "true" : "false" : void 0
|
|
949
|
+
}, [on("click", props.onClick, (onClick) => () => {
|
|
950
|
+
if (!onClick() && ctx.shouldReturnFocusToEditor()) ctx.view.focus();
|
|
951
|
+
})]);
|
|
952
|
+
return createOptionalTooltip(ctx, {
|
|
953
|
+
enabled: () => Boolean(ctx.evalProp(props.icon) && !ctx.evalProp(props.noTooltip) && !disabled()),
|
|
954
|
+
label: props.label,
|
|
955
|
+
anchor: button,
|
|
956
|
+
slot: props.slot
|
|
957
|
+
});
|
|
958
|
+
};
|
|
959
|
+
var createMenu = (ctx, props) => {
|
|
960
|
+
const menu = ctx.rte.createComponent(require_definition$4.Menu);
|
|
961
|
+
ctx.bindToEl(menu, {
|
|
962
|
+
className: "ui-menu",
|
|
963
|
+
autoDismiss: true,
|
|
964
|
+
ariaLabel: props.label,
|
|
965
|
+
anchor: wrapperTargets.get(props.trigger) ?? props.trigger,
|
|
966
|
+
placement: ctx.props.popupPlacement,
|
|
967
|
+
offset: () => ctx.evalProp(ctx.props.menuOffset) ?? null
|
|
968
|
+
}, [], props.children);
|
|
969
|
+
const fragment = document.createDocumentFragment();
|
|
970
|
+
fragment.appendChild(props.trigger);
|
|
971
|
+
fragment.appendChild(menu);
|
|
972
|
+
return fragment;
|
|
973
|
+
};
|
|
974
|
+
var createMenuItem = (ctx, props) => {
|
|
975
|
+
const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled) || ctx.evalProp(props.disabled));
|
|
976
|
+
const item = ctx.rte.createComponent(require_definition$3.MenuItem);
|
|
977
|
+
ctx.bindToEl(item, {
|
|
978
|
+
text: props.text,
|
|
979
|
+
checked: props.checked,
|
|
980
|
+
disabled,
|
|
981
|
+
controlType: "radio",
|
|
982
|
+
checkedAppearance: "tick-only"
|
|
983
|
+
}, [on("change", props.onSelect, (onSelect) => () => {
|
|
984
|
+
if (item.checked && !item.disabled) {
|
|
985
|
+
if (ctx.evalProp(props.checked) !== item.checked) {
|
|
986
|
+
onSelect();
|
|
987
|
+
if (ctx.shouldReturnFocusToEditor()) ctx.view.focus();
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
})]);
|
|
991
|
+
return item;
|
|
992
|
+
};
|
|
993
|
+
var createButtonGroup = (ctx, props) => createDiv(ctx, {
|
|
994
|
+
slot: props.slot,
|
|
995
|
+
className: "ui-button-group",
|
|
996
|
+
children: props.children
|
|
997
|
+
});
|
|
998
|
+
function markActive(state, type) {
|
|
999
|
+
const { from, $from, to, empty } = state.selection;
|
|
1000
|
+
if (empty) return !!type.isInSet(state.storedMarks || $from.marks());
|
|
1001
|
+
else return state.doc.rangeHasMark(from, to, type);
|
|
1002
|
+
}
|
|
1003
|
+
var createMarkToggle = (ctx, props) => createButton(ctx, {
|
|
1004
|
+
label: props.label,
|
|
1005
|
+
icon: props.icon,
|
|
1006
|
+
active: () => markActive(ctx.view.state, props.markType),
|
|
1007
|
+
disabled: () => !(0, prosemirror_commands.toggleMark)(props.markType)(ctx.view.state),
|
|
1008
|
+
onClick: () => {
|
|
1009
|
+
(0, prosemirror_commands.toggleMark)(props.markType)(ctx.view.state, ctx.view.dispatch);
|
|
1010
|
+
}
|
|
1011
|
+
});
|
|
1012
|
+
var createOption = (ctx, props) => {
|
|
1013
|
+
const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled) || ctx.evalProp(props.disabled));
|
|
1014
|
+
const option = ctx.rte.createComponent(require_definition$7.ListboxOption);
|
|
1015
|
+
ctx.bindToEl(option, {
|
|
1016
|
+
value: props.value,
|
|
1017
|
+
text: props.text,
|
|
1018
|
+
disabled
|
|
1019
|
+
});
|
|
1020
|
+
return option;
|
|
1021
|
+
};
|
|
1022
|
+
var createSelect = (ctx, props) => {
|
|
1023
|
+
const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled));
|
|
1024
|
+
const select = ctx.rte.createComponent(require_definition$9.Select);
|
|
1025
|
+
select.setAttribute("data-class", "ui-select");
|
|
1026
|
+
ctx.bindToEl(select, {
|
|
1027
|
+
placeholder: " ",
|
|
1028
|
+
ariaLabel: props.label,
|
|
1029
|
+
appearance: "ghost",
|
|
1030
|
+
disabled
|
|
1031
|
+
}, [on("change", props.onSelect, (onSelect) => () => {
|
|
1032
|
+
const value = select.value;
|
|
1033
|
+
if (value) {
|
|
1034
|
+
onSelect(value);
|
|
1035
|
+
if (ctx.shouldReturnFocusToEditor()) ctx.view.focus();
|
|
1036
|
+
}
|
|
1037
|
+
})], props.children);
|
|
1038
|
+
queueMicrotask(() => {
|
|
1039
|
+
ctx.bindProp(props.value, (value) => select.value = value);
|
|
1040
|
+
});
|
|
1041
|
+
let hideTooltip = false;
|
|
1042
|
+
select.addEventListener("vwc-popup:open", () => {
|
|
1043
|
+
hideTooltip = true;
|
|
1044
|
+
ctx.updateBindings();
|
|
1045
|
+
});
|
|
1046
|
+
select.addEventListener("vwc-popup:close", () => {
|
|
1047
|
+
hideTooltip = false;
|
|
1048
|
+
ctx.updateBindings();
|
|
1049
|
+
});
|
|
1050
|
+
return createOptionalTooltip(ctx, {
|
|
1051
|
+
enabled: () => !hideTooltip && !disabled(),
|
|
1052
|
+
label: props.label,
|
|
1053
|
+
anchor: select
|
|
1054
|
+
});
|
|
1055
|
+
};
|
|
1056
|
+
var createDivider = (ctx) => {
|
|
1057
|
+
const divider = ctx.rte.createComponent(require_divider.Divider);
|
|
1058
|
+
ctx.bindToEl(divider, {
|
|
1059
|
+
className: "ui-divider",
|
|
1060
|
+
orientation: "vertical"
|
|
1061
|
+
});
|
|
1062
|
+
return divider;
|
|
1063
|
+
};
|
|
1064
|
+
var createTextField = (ctx, props) => {
|
|
1065
|
+
const textField = ctx.rte.createComponent(require_definition$5.TextField);
|
|
1066
|
+
ctx.bindToEl(textField, {
|
|
1067
|
+
label: props.label,
|
|
1068
|
+
value: props.value,
|
|
1069
|
+
placeholder: props.placeholder,
|
|
1070
|
+
slot: props.slot,
|
|
1071
|
+
autofocus: props.autofocus,
|
|
1072
|
+
type: props.type,
|
|
1073
|
+
helperText: props.helperText
|
|
1074
|
+
}, [on("input", props.onInput, (onInput) => () => {
|
|
1075
|
+
onInput(textField.value);
|
|
1076
|
+
})]);
|
|
1077
|
+
return textField;
|
|
1021
1078
|
};
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
this.specs = specs;
|
|
1027
|
-
}
|
|
1028
|
-
getAllowedMarksForNode(nodeName) {
|
|
1029
|
-
return this.specs.filter((spec) => !spec.onNodeName || spec.onNodeName === nodeName).map((spec) => spec.markName);
|
|
1030
|
-
}
|
|
1031
|
-
getReferencedNodeNames() {
|
|
1032
|
-
return new Set(this.specs.map((spec) => spec.onNodeName).filter(Boolean));
|
|
1033
|
-
}
|
|
1079
|
+
var createText = (ctx, props) => {
|
|
1080
|
+
const textNode = document.createTextNode("");
|
|
1081
|
+
ctx.bindToEl(textNode, { textContent: props.text });
|
|
1082
|
+
return textNode;
|
|
1034
1083
|
};
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
const
|
|
1046
|
-
if (
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
const result = [];
|
|
1061
|
-
const active = [];
|
|
1062
|
-
const activeContents = [result];
|
|
1063
|
-
for (const node of nodes) {
|
|
1064
|
-
const nodeMarks = [...node.marks ?? []].sort((a, b) => getMarkRank(a) - getMarkRank(b));
|
|
1065
|
-
let keep = 0;
|
|
1066
|
-
let rendered = 0;
|
|
1067
|
-
while (keep < active.length && rendered < nodeMarks.length) {
|
|
1068
|
-
if (!marksEqual(active[keep], nodeMarks[rendered])) break;
|
|
1069
|
-
keep++;
|
|
1070
|
-
rendered++;
|
|
1071
|
-
}
|
|
1072
|
-
while (active.length > keep) {
|
|
1073
|
-
active.pop();
|
|
1074
|
-
activeContents.pop();
|
|
1075
|
-
}
|
|
1076
|
-
while (rendered < nodeMarks.length) {
|
|
1077
|
-
const mark = nodeMarks[rendered];
|
|
1078
|
-
const markContent = [];
|
|
1079
|
-
activeContents[activeContents.length - 1].push({
|
|
1080
|
-
type: "mark",
|
|
1081
|
-
mark,
|
|
1082
|
-
children: {
|
|
1083
|
-
type: "fragment",
|
|
1084
|
-
content: markContent,
|
|
1085
|
-
[require_slottable_request.impl]: ctx
|
|
1086
|
-
},
|
|
1087
|
-
[require_slottable_request.impl]: ctx
|
|
1088
|
-
});
|
|
1089
|
-
active.push(mark);
|
|
1090
|
-
activeContents.push(markContent);
|
|
1091
|
-
rendered++;
|
|
1092
|
-
}
|
|
1093
|
-
activeContents[activeContents.length - 1].push({
|
|
1094
|
-
type: "node",
|
|
1095
|
-
node,
|
|
1096
|
-
children: convertFragment(node.content ?? []),
|
|
1097
|
-
[require_slottable_request.impl]: ctx
|
|
1084
|
+
var createSingleSlot = (ctx, props) => {
|
|
1085
|
+
const slot = document.createElement("slot");
|
|
1086
|
+
slot.name = props.name;
|
|
1087
|
+
let currentEl = null;
|
|
1088
|
+
const listeners = [];
|
|
1089
|
+
function cleanup() {
|
|
1090
|
+
for (const { el, type, handler } of listeners) el.removeEventListener(type, handler);
|
|
1091
|
+
listeners.length = 0;
|
|
1092
|
+
currentEl = null;
|
|
1093
|
+
}
|
|
1094
|
+
for (const [key, prop] of Object.entries(props.assignedProps)) ctx.bindProp(prop, (value) => {
|
|
1095
|
+
if (currentEl) currentEl[key] = value;
|
|
1096
|
+
});
|
|
1097
|
+
function applyPropsAndEvents(el) {
|
|
1098
|
+
for (const [key, prop] of Object.entries(props.assignedProps)) el[key] = ctx.evalProp(prop);
|
|
1099
|
+
for (const [type, handler] of Object.entries(props.assignedEvents)) {
|
|
1100
|
+
const listener = (e) => {
|
|
1101
|
+
handler(e);
|
|
1102
|
+
if (ctx.shouldReturnFocusToEditor()) ctx.view.focus();
|
|
1103
|
+
};
|
|
1104
|
+
el.addEventListener(type, listener);
|
|
1105
|
+
listeners.push({
|
|
1106
|
+
el,
|
|
1107
|
+
type,
|
|
1108
|
+
handler: listener
|
|
1098
1109
|
});
|
|
1099
1110
|
}
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
children: convertFragment(doc.content),
|
|
1110
|
-
[require_slottable_request.impl]: ctx
|
|
1111
|
+
}
|
|
1112
|
+
const processSlot = () => {
|
|
1113
|
+
const first = slot.assignedElements({ flatten: true })[0];
|
|
1114
|
+
if (first === currentEl) return;
|
|
1115
|
+
cleanup();
|
|
1116
|
+
if (first) {
|
|
1117
|
+
currentEl = first;
|
|
1118
|
+
applyPropsAndEvents(first);
|
|
1119
|
+
}
|
|
1111
1120
|
};
|
|
1121
|
+
slot.addEventListener("slotchange", processSlot);
|
|
1122
|
+
queueMicrotask(processSlot);
|
|
1123
|
+
return slot;
|
|
1112
1124
|
};
|
|
1113
1125
|
//#endregion
|
|
1114
|
-
//#region src/lib/rich-text-editor/rte/
|
|
1115
|
-
var
|
|
1116
|
-
constructor(
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
return new RteInstance(this, options);
|
|
1126
|
+
//#region src/lib/rich-text-editor/rte/features/internal/history.ts
|
|
1127
|
+
var RteHistoryFeatureImpl = class extends require_slottable_request.RteFeatureImpl {
|
|
1128
|
+
constructor(config) {
|
|
1129
|
+
super();
|
|
1130
|
+
this.config = config;
|
|
1131
|
+
this.name = "RteHistoryFeature";
|
|
1121
1132
|
}
|
|
1122
|
-
|
|
1123
|
-
return
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1133
|
+
getPlugins() {
|
|
1134
|
+
return [this.contribution((0, prosemirror_history.history)()), this.contribution((0, prosemirror_keymap.keymap)({
|
|
1135
|
+
"Mod-z": prosemirror_history.undo,
|
|
1136
|
+
"Ctrl-y": prosemirror_history.redo,
|
|
1137
|
+
"Cmd-Z": prosemirror_history.redo
|
|
1138
|
+
}))];
|
|
1127
1139
|
}
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
marks: {}
|
|
1154
|
-
};
|
|
1155
|
-
for (const schema of schemaContributions) {
|
|
1156
|
-
Object.assign(schemaSpec.nodes, schema.nodes ?? {});
|
|
1157
|
-
Object.assign(schemaSpec.marks, schema.marks ?? {});
|
|
1158
|
-
}
|
|
1159
|
-
for (const referencedNodeName of this.textblockMarks.getReferencedNodeNames()) if (!(referencedNodeName in schemaSpec.nodes)) throw new Error(`Unknown node "${referencedNodeName}"`);
|
|
1160
|
-
this.schema = new prosemirror_model.Schema(schemaSpec);
|
|
1140
|
+
getToolbarItems() {
|
|
1141
|
+
if (!this.config.showToolbarButtons) return [];
|
|
1142
|
+
return [this.contribution({
|
|
1143
|
+
section: "history",
|
|
1144
|
+
render: (ctx) => createButton(ctx, {
|
|
1145
|
+
label: (ctx) => ctx.rte.getLocale().richTextEditor.undo,
|
|
1146
|
+
icon: "undo-line",
|
|
1147
|
+
disabled: (ctx) => !(0, prosemirror_history.undo)(ctx.view.state),
|
|
1148
|
+
onClick: () => {
|
|
1149
|
+
const { state, dispatch } = ctx.view;
|
|
1150
|
+
(0, prosemirror_history.undo)(state, dispatch);
|
|
1151
|
+
}
|
|
1152
|
+
})
|
|
1153
|
+
}, 1), this.contribution({
|
|
1154
|
+
section: "history",
|
|
1155
|
+
render: (ctx) => createButton(ctx, {
|
|
1156
|
+
label: (ctx) => ctx.rte.getLocale().richTextEditor.redo,
|
|
1157
|
+
icon: "redo-line",
|
|
1158
|
+
disabled: (ctx) => !(0, prosemirror_history.redo)(ctx.view.state),
|
|
1159
|
+
onClick: () => {
|
|
1160
|
+
const { state, dispatch } = ctx.view;
|
|
1161
|
+
(0, prosemirror_history.redo)(state, dispatch);
|
|
1162
|
+
}
|
|
1163
|
+
})
|
|
1164
|
+
}, 2)];
|
|
1161
1165
|
}
|
|
1162
1166
|
};
|
|
1163
1167
|
//#endregion
|
|
@@ -1343,6 +1347,7 @@ var RteBaseImpl = class extends require_slottable_request.RteFeatureImpl {
|
|
|
1343
1347
|
return [
|
|
1344
1348
|
this,
|
|
1345
1349
|
new RteCoreImpl(),
|
|
1350
|
+
new RteHistoryFeatureImpl({ showToolbarButtons: this.config?.historyToolbarButtons ?? true }),
|
|
1346
1351
|
new RteBasicTextBlocksImpl({
|
|
1347
1352
|
heading1: this.config?.heading1 ?? false,
|
|
1348
1353
|
heading2: this.config?.heading2 ?? false,
|
|
@@ -1423,12 +1428,28 @@ var RteToolbarFeatureImpl = class extends require_slottable_request.RteFeatureIm
|
|
|
1423
1428
|
for (const toolbarItem of require_slottable_request.sortedContributions(rte.features.flatMap((f) => f.getToolbarItems(rte)))) itemsBySection.get(toolbarItem.section).push(toolbarItem);
|
|
1424
1429
|
const core = rte.getFeature("RteCore");
|
|
1425
1430
|
return [this.contribution(this.hidden.plugin), this.contribution(new prosemirror_state.Plugin({ view: (view) => {
|
|
1431
|
+
let focusCameFromEditorWithMouse = false;
|
|
1426
1432
|
const ctx = new UiCtx(view, rte, {
|
|
1427
1433
|
popupPlacement: this.config?.popupDirection === "outward" ? "bottom" : "top",
|
|
1428
1434
|
menuOffset: 8,
|
|
1429
|
-
disabled: () => core.disabled.getValue(rte)
|
|
1435
|
+
disabled: () => core.disabled.getValue(rte),
|
|
1436
|
+
shouldReturnFocusToEditor: () => focusCameFromEditorWithMouse
|
|
1430
1437
|
});
|
|
1431
1438
|
const toolbar = view.dom.getRootNode().querySelector(".toolbar");
|
|
1439
|
+
let mouseDownTime = -Infinity;
|
|
1440
|
+
const onMouseDown = () => {
|
|
1441
|
+
if (view.hasFocus()) focusCameFromEditorWithMouse = true;
|
|
1442
|
+
mouseDownTime = Date.now();
|
|
1443
|
+
};
|
|
1444
|
+
const onFocusIn = () => {
|
|
1445
|
+
if (Date.now() - mouseDownTime > 250) focusCameFromEditorWithMouse = false;
|
|
1446
|
+
};
|
|
1447
|
+
const onFocusOut = (e) => {
|
|
1448
|
+
if (!toolbar.contains(e.relatedTarget)) focusCameFromEditorWithMouse = false;
|
|
1449
|
+
};
|
|
1450
|
+
toolbar.addEventListener("mousedown", onMouseDown, { capture: true });
|
|
1451
|
+
toolbar.addEventListener("focusin", onFocusIn);
|
|
1452
|
+
toolbar.addEventListener("focusout", onFocusOut);
|
|
1432
1453
|
ctx.bindProp(() => this.hidden.getValue(rte), (hidden) => {
|
|
1433
1454
|
toolbar.classList.toggle("toolbar--hidden", hidden);
|
|
1434
1455
|
});
|
|
@@ -1450,6 +1471,10 @@ var RteToolbarFeatureImpl = class extends require_slottable_request.RteFeatureIm
|
|
|
1450
1471
|
},
|
|
1451
1472
|
destroy() {
|
|
1452
1473
|
toolbar.innerHTML = "";
|
|
1474
|
+
toolbar.removeEventListener("mousedown", onMouseDown, { capture: true });
|
|
1475
|
+
toolbar.removeEventListener("focusin", onFocusIn);
|
|
1476
|
+
toolbar.removeEventListener("focusout", onFocusOut);
|
|
1477
|
+
focusCameFromEditorWithMouse = false;
|
|
1453
1478
|
}
|
|
1454
1479
|
};
|
|
1455
1480
|
} }))];
|
|
@@ -2402,7 +2427,10 @@ var RteLinkFeatureImpl = class extends require_slottable_request.RteFeatureImpl
|
|
|
2402
2427
|
return null;
|
|
2403
2428
|
} },
|
|
2404
2429
|
view: (view) => {
|
|
2405
|
-
const ctx = new UiCtx(view, rte, {
|
|
2430
|
+
const ctx = new UiCtx(view, rte, {
|
|
2431
|
+
popupPlacement: "bottom",
|
|
2432
|
+
shouldReturnFocusToEditor: true
|
|
2433
|
+
});
|
|
2406
2434
|
popup = rte.createComponent(Popover);
|
|
2407
2435
|
popup.anchorId = "current-link";
|
|
2408
2436
|
const content = createDiv(ctx, {
|
|
@@ -2527,6 +2555,7 @@ var RteLinkFeatureImpl = class extends require_slottable_request.RteFeatureImpl
|
|
|
2527
2555
|
onClick: () => {
|
|
2528
2556
|
const { state, dispatch } = ctx.view;
|
|
2529
2557
|
this.insertLink(rte, urlField.value, textField.value)(state, dispatch);
|
|
2558
|
+
this.toolbarMenu.open = false;
|
|
2530
2559
|
}
|
|
2531
2560
|
});
|
|
2532
2561
|
const updateValidation = () => {
|
|
@@ -2777,7 +2806,10 @@ var RteInlineImageFeatureImpl = class extends require_slottable_request.RteFeatu
|
|
|
2777
2806
|
return [this.contribution(new prosemirror_state.Plugin({
|
|
2778
2807
|
props: { nodeViews: { inlineImage: (node, view, getPos) => new InlineImageView(node, view, getPos, this.config) } },
|
|
2779
2808
|
view: (view) => {
|
|
2780
|
-
const ctx = new UiCtx(view, rte, {
|
|
2809
|
+
const ctx = new UiCtx(view, rte, {
|
|
2810
|
+
popupPlacement: "bottom",
|
|
2811
|
+
shouldReturnFocusToEditor: true
|
|
2812
|
+
});
|
|
2781
2813
|
popover = rte.createComponent(Popover);
|
|
2782
2814
|
popover.offset = 4;
|
|
2783
2815
|
const content = createDiv(ctx, {
|
|
@@ -3195,7 +3227,6 @@ var RteInputRuleFeature = require_slottable_request.featureFacade(RteInputRuleFe
|
|
|
3195
3227
|
//#endregion
|
|
3196
3228
|
//#region src/lib/rich-text-editor/rte/features/keyboard-shortcuts.ts
|
|
3197
3229
|
function toCommand(handler, rteInstance) {
|
|
3198
|
-
if (handler.length === 0) return (_state, _dispatch) => handler();
|
|
3199
3230
|
return (_state, _dispatch) => handler(rteInstance);
|
|
3200
3231
|
}
|
|
3201
3232
|
var RteKeyboardShortcutsFeatureImpl = class extends require_slottable_request.RteFeatureImpl {
|
|
@@ -3358,7 +3389,10 @@ var RteSuggestFeatureImpl = class extends require_slottable_request.RteFeatureIm
|
|
|
3358
3389
|
return suggestState.loadingDecoration.add(state.doc, [prosemirror_view.Decoration.inline(suggestState.match.start, suggestState.match.end, { id: `suggest-anchor-${this.featureId}` })]);
|
|
3359
3390
|
} },
|
|
3360
3391
|
view: (view) => {
|
|
3361
|
-
const ctx = new UiCtx(view, rte, {
|
|
3392
|
+
const ctx = new UiCtx(view, rte, {
|
|
3393
|
+
popupPlacement: "bottom",
|
|
3394
|
+
shouldReturnFocusToEditor: true
|
|
3395
|
+
});
|
|
3362
3396
|
const popover = rte.createComponent(Popover);
|
|
3363
3397
|
popover.anchorId = `suggest-anchor-${this.featureId}`;
|
|
3364
3398
|
popover.kind = "autocomplete";
|