@vonage/vivid 5.20.1 → 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.
Files changed (64) hide show
  1. package/alert/index.cjs +1 -1
  2. package/alert/index.js +1 -1
  3. package/banner/index.cjs +1 -1
  4. package/banner/index.js +1 -1
  5. package/bundled/definition18.cjs +1 -1
  6. package/bundled/definition18.js +1 -1
  7. package/bundled/definition6.cjs +1 -1
  8. package/bundled/definition6.js +1 -1
  9. package/bundled/vivid-element.cjs +1 -1
  10. package/bundled/vivid-element.js +1 -1
  11. package/combobox/index.cjs +1 -1
  12. package/combobox/index.js +1 -1
  13. package/custom-elements.json +2479 -2479
  14. package/file-picker/index.cjs +1 -1
  15. package/file-picker/index.js +1 -1
  16. package/lib/rich-text-editor/rte/features/base.d.ts +1 -0
  17. package/lib/rich-text-editor/rte/features/internal/history.d.ts +6 -0
  18. package/locales/de-DE.cjs +178 -4
  19. package/locales/de-DE.js +179 -2
  20. package/locales/en-GB.cjs +9 -4
  21. package/locales/en-GB.js +10 -2
  22. package/locales/en-US.cjs +2 -270
  23. package/locales/en-US.js +1 -267
  24. package/locales/ja-JP.cjs +171 -4
  25. package/locales/ja-JP.js +172 -2
  26. package/locales/zh-CN.cjs +172 -4
  27. package/locales/zh-CN.js +173 -2
  28. package/package.json +6 -5
  29. package/rich-text-editor/index.cjs +12 -12
  30. package/rich-text-editor/index.js +1015 -1015
  31. package/searchable-select/index.cjs +1 -1
  32. package/searchable-select/index.js +1 -1
  33. package/switch/index.cjs +1 -1
  34. package/switch/index.js +1 -1
  35. package/tabs/index.cjs +1 -1
  36. package/tabs/index.js +1 -1
  37. package/unbundled/chunk.cjs +15 -0
  38. package/unbundled/chunk.js +13 -0
  39. package/unbundled/definition17.cjs +1 -1
  40. package/unbundled/definition17.js +1 -1
  41. package/unbundled/definition30.cjs +1 -1
  42. package/unbundled/definition30.js +1 -1
  43. package/unbundled/definition43.cjs +1 -1
  44. package/unbundled/definition43.js +1 -1
  45. package/unbundled/definition60.cjs +608 -607
  46. package/unbundled/definition60.js +608 -607
  47. package/unbundled/definition61.cjs +1 -1
  48. package/unbundled/definition61.js +1 -1
  49. package/unbundled/definition63.cjs +1 -1
  50. package/unbundled/definition63.js +1 -1
  51. package/unbundled/definition69.cjs +1 -1
  52. package/unbundled/definition69.js +1 -1
  53. package/unbundled/definition7.cjs +1 -1
  54. package/unbundled/definition7.js +1 -1
  55. package/unbundled/definition73.cjs +1 -1
  56. package/unbundled/definition73.js +1 -1
  57. package/unbundled/definition9.cjs +1 -1
  58. package/unbundled/definition9.js +1 -1
  59. package/unbundled/en-US.cjs +449 -0
  60. package/unbundled/en-US.js +445 -0
  61. package/unbundled/localized.cjs +2 -2
  62. package/unbundled/localized.js +1 -1
  63. package/unbundled/vivid-element.cjs +1 -1
  64. 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,330 @@ 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/utils/ui.ts
244
- var isPropBinding = (prop) => typeof prop === "function";
245
- var on = (event, prop, handler) => [
246
- event,
247
- prop,
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 = [];
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";
256
248
  }
257
- evalProp(prop) {
258
- return isPropBinding(prop) ? prop(this) : 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
+ } }))];
259
255
  }
260
- bindProp(prop, bindFn) {
261
- if (prop === void 0) return;
262
- else if (isPropBinding(prop)) {
263
- const binding = () => bindFn(prop(this));
264
- this.bindings.push(binding);
265
- binding();
266
- } else bindFn(prop);
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";
267
273
  }
268
- updateBindings() {
269
- for (const binding of this.bindings) binding();
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
+ } } }))];
270
296
  }
271
- shouldReturnFocusToEditor() {
272
- return this.evalProp(this.props.shouldReturnFocusToEditor);
297
+ };
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;
273
312
  }
274
- bindToEl(target, props = {}, events = [], children = []) {
275
- for (const name in props) this.bindProp(props[name], (value) => {
276
- target[name] = value;
277
- });
278
- for (const [name, value, bindFn] of events) if (value) target.addEventListener(name, bindFn(value));
279
- for (const child of children) target.appendChild(child);
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
+ ];
280
380
  }
281
381
  };
282
- var createDiv = (ctx, props) => {
283
- const div = document.createElement("div");
284
- ctx.bindToEl(div, {
285
- className: props.className,
286
- slot: props.slot
287
- }, [], props.children);
288
- return div;
289
- };
290
- var createAnchor = (ctx, props) => {
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 "";
291
395
  const anchor = document.createElement("a");
292
- ctx.bindToEl(anchor, {
293
- href: props.href,
294
- target: props.target,
295
- rel: props.rel,
296
- className: props.className
297
- }, [], props.children);
298
- 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") ?? "";
299
402
  };
300
- var wrapperTargets = /* @__PURE__ */ new WeakMap();
301
- var createOptionalTooltip = (ctx, props) => {
302
- const tooltip = ctx.rte.createComponent(require_definition$6.Tooltip);
303
- tooltip.setAttribute("exportparts", "vvd-theme-alternate");
304
- ctx.bindToEl(tooltip, {
305
- className: "ui-tooltip",
306
- text: props.label,
307
- placement: ctx.props.popupPlacement
308
- });
309
- ctx.bindProp(props.enabled, (enabled) => {
310
- if (!enabled) tooltip.open = false;
311
- tooltip.anchor = enabled ? props.anchor : void 0;
312
- });
313
- const wrapper = createDiv(ctx, {
314
- className: "ui-tooltip-wrapper",
315
- slot: props.slot,
316
- children: [props.anchor, tooltip]
317
- });
318
- wrapperTargets.set(wrapper, props.anchor);
319
- 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") ?? "";
320
414
  };
321
- var createButton = (ctx, props) => {
322
- const variant = () => ctx.evalProp(props.variant) ?? "toolbar";
323
- const size = () => variant() === "toolbar" ? "super-condensed" : "condensed";
324
- const appearanceInactive = () => variant() === "popover-primary" ? "outlined" : "ghost-light";
325
- const appearanceActive = () => variant() === "toolbar-menu" || variant() === "popover" ? "filled" : appearanceInactive();
326
- const appearance = () => ctx.evalProp(props.active) ? appearanceActive() : appearanceInactive();
327
- const connotation = () => ctx.evalProp(props.connotation) ?? (variant() === "toolbar-menu" && ctx.evalProp(props.active) ? "cta" : void 0);
328
- const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled) || ctx.evalProp(props.disabled));
329
- const button = ctx.rte.createComponent(require_definition$1.Button);
330
- ctx.bindToEl(button, {
331
- size,
332
- appearance,
333
- disabled,
334
- icon: props.icon,
335
- autofocus: props.autofocus,
336
- connotation,
337
- [props.icon ? "ariaLabel" : "label"]: props.label,
338
- pressed: () => Boolean(ctx.evalProp(props.active)),
339
- ariaPressed: props.active !== void 0 ? () => ctx.evalProp(props.active) ? "true" : "false" : void 0
340
- }, [on("click", props.onClick, (onClick) => () => {
341
- if (!onClick() && ctx.shouldReturnFocusToEditor()) ctx.view.focus();
342
- })]);
343
- return createOptionalTooltip(ctx, {
344
- enabled: () => Boolean(ctx.evalProp(props.icon) && !ctx.evalProp(props.noTooltip) && !disabled()),
345
- label: props.label,
346
- anchor: button,
347
- slot: props.slot
348
- });
349
- };
350
- var createMenu = (ctx, props) => {
351
- const menu = ctx.rte.createComponent(require_definition$4.Menu);
352
- ctx.bindToEl(menu, {
353
- className: "ui-menu",
354
- autoDismiss: true,
355
- ariaLabel: props.label,
356
- anchor: wrapperTargets.get(props.trigger) ?? props.trigger,
357
- placement: ctx.props.popupPlacement,
358
- offset: () => ctx.evalProp(ctx.props.menuOffset) ?? null
359
- }, [], props.children);
360
- const fragment = document.createDocumentFragment();
361
- fragment.appendChild(props.trigger);
362
- fragment.appendChild(menu);
363
- return fragment;
364
- };
365
- var createMenuItem = (ctx, props) => {
366
- const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled) || ctx.evalProp(props.disabled));
367
- const item = ctx.rte.createComponent(require_definition$3.MenuItem);
368
- ctx.bindToEl(item, {
369
- text: props.text,
370
- checked: props.checked,
371
- disabled,
372
- controlType: "radio",
373
- checkedAppearance: "tick-only"
374
- }, [on("change", props.onSelect, (onSelect) => () => {
375
- if (item.checked && !item.disabled) {
376
- if (ctx.evalProp(props.checked) !== item.checked) {
377
- onSelect();
378
- if (ctx.shouldReturnFocusToEditor()) ctx.view.focus();
379
- }
380
- }
381
- })]);
382
- return item;
383
- };
384
- var createButtonGroup = (ctx, props) => createDiv(ctx, {
385
- slot: props.slot,
386
- className: "ui-button-group",
387
- 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)]))
388
426
  });
389
- function markActive(state, type) {
390
- const { from, $from, to, empty } = state.selection;
391
- if (empty) return !!type.isInSet(state.storedMarks || $from.marks());
392
- else return state.doc.rangeHasMark(from, to, type);
393
- }
394
- var createMarkToggle = (ctx, props) => createButton(ctx, {
395
- label: props.label,
396
- icon: props.icon,
397
- active: () => markActive(ctx.view.state, props.markType),
398
- disabled: () => !(0, prosemirror_commands.toggleMark)(props.markType)(ctx.view.state),
399
- onClick: () => {
400
- (0, prosemirror_commands.toggleMark)(props.markType)(ctx.view.state, ctx.view.dispatch);
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;
401
452
  }
402
- });
403
- var createOption = (ctx, props) => {
404
- const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled) || ctx.evalProp(props.disabled));
405
- const option = ctx.rte.createComponent(require_definition$7.ListboxOption);
406
- ctx.bindToEl(option, {
407
- value: props.value,
408
- text: props.text,
409
- disabled
410
- });
411
- return option;
412
453
  };
413
- var createSelect = (ctx, props) => {
414
- const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled));
415
- const select = ctx.rte.createComponent(require_definition$9.Select);
416
- select.setAttribute("data-class", "ui-select");
417
- ctx.bindToEl(select, {
418
- placeholder: " ",
419
- ariaLabel: props.label,
420
- appearance: "ghost",
421
- disabled
422
- }, [on("change", props.onSelect, (onSelect) => () => {
423
- const value = select.value;
424
- if (value) {
425
- onSelect(value);
426
- if (ctx.shouldReturnFocusToEditor()) ctx.view.focus();
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;
427
472
  }
428
- })], props.children);
429
- queueMicrotask(() => {
430
- ctx.bindProp(props.value, (value) => select.value = value);
431
- });
432
- let hideTooltip = false;
433
- select.addEventListener("vwc-popup:open", () => {
434
- hideTooltip = true;
435
- ctx.updateBindings();
436
- });
437
- select.addEventListener("vwc-popup:close", () => {
438
- hideTooltip = false;
439
- 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;
440
478
  });
441
- return createOptionalTooltip(ctx, {
442
- enabled: () => !hideTooltip && !disabled(),
443
- label: props.label,
444
- 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;
445
482
  });
446
- };
447
- var createDivider = (ctx) => {
448
- const divider = ctx.rte.createComponent(require_divider.Divider);
449
- ctx.bindToEl(divider, {
450
- className: "ui-divider",
451
- orientation: "vertical"
483
+ parserRules.push({
484
+ tag: "br",
485
+ closeParent: true
452
486
  });
453
- return divider;
454
- };
455
- var createTextField = (ctx, props) => {
456
- const textField = ctx.rte.createComponent(require_definition$5.TextField);
457
- ctx.bindToEl(textField, {
458
- label: props.label,
459
- value: props.value,
460
- placeholder: props.placeholder,
461
- slot: props.slot,
462
- autofocus: props.autofocus,
463
- type: props.type,
464
- helperText: props.helperText
465
- }, [on("input", props.onInput, (onInput) => () => {
466
- onInput(textField.value);
467
- })]);
468
- return textField;
469
- };
470
- var createText = (ctx, props) => {
471
- const textNode = document.createTextNode("");
472
- ctx.bindToEl(textNode, { textContent: props.text });
473
- return textNode;
487
+ return new prosemirror_model.DOMParser(schema, parserRules);
474
488
  };
475
- var createSingleSlot = (ctx, props) => {
476
- const slot = document.createElement("slot");
477
- slot.name = props.name;
478
- let currentEl = null;
479
- const listeners = [];
480
- function cleanup() {
481
- for (const { el, type, handler } of listeners) el.removeEventListener(type, handler);
482
- listeners.length = 0;
483
- 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);
484
494
  }
485
- for (const [key, prop] of Object.entries(props.assignedProps)) ctx.bindProp(prop, (value) => {
486
- if (currentEl) currentEl[key] = value;
487
- });
488
- function applyPropsAndEvents(el) {
489
- for (const [key, prop] of Object.entries(props.assignedProps)) el[key] = ctx.evalProp(prop);
490
- for (const [type, handler] of Object.entries(props.assignedEvents)) {
491
- const listener = (e) => {
492
- handler(e);
493
- if (ctx.shouldReturnFocusToEditor()) ctx.view.focus();
494
- };
495
- el.addEventListener(type, listener);
496
- listeners.push({
497
- el,
498
- type,
499
- handler: listener
500
- });
501
- }
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);
502
506
  }
503
- const processSlot = () => {
504
- const first = slot.assignedElements({ flatten: true })[0];
505
- if (first === currentEl) return;
506
- cleanup();
507
- if (first) {
508
- currentEl = first;
509
- applyPropsAndEvents(first);
510
- }
511
- };
512
- slot.addEventListener("slotchange", processSlot);
513
- queueMicrotask(processSlot);
514
- return slot;
515
507
  };
516
- //#endregion
517
- //#region src/lib/rich-text-editor/rte/features/internal/history.ts
518
- var RteHistoryFeatureImpl = class extends require_slottable_request.RteFeatureImpl {
519
- constructor(..._args) {
520
- super(..._args);
521
- this.name = "RteHistoryFeature";
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);
522
515
  }
523
- getPlugins() {
524
- return [this.contribution((0, prosemirror_history.history)()), this.contribution((0, prosemirror_keymap.keymap)({
525
- "Mod-z": prosemirror_history.undo,
526
- "Ctrl-y": prosemirror_history.redo,
527
- "Cmd-Z": prosemirror_history.redo
528
- }))];
529
- }
530
- getToolbarItems() {
531
- return [this.contribution({
532
- section: "history",
533
- render: (ctx) => createButton(ctx, {
534
- label: (ctx) => ctx.rte.getLocale().richTextEditor.undo,
535
- icon: "undo-line",
536
- disabled: (ctx) => !(0, prosemirror_history.undo)(ctx.view.state),
537
- onClick: () => {
538
- const { state, dispatch } = ctx.view;
539
- (0, prosemirror_history.undo)(state, dispatch);
540
- }
541
- })
542
- }, 1), this.contribution({
543
- section: "history",
544
- render: (ctx) => createButton(ctx, {
545
- label: (ctx) => ctx.rte.getLocale().richTextEditor.redo,
546
- icon: "redo-line",
547
- disabled: (ctx) => !(0, prosemirror_history.redo)(ctx.view.state),
548
- onClick: () => {
549
- const { state, dispatch } = ctx.view;
550
- (0, prosemirror_history.redo)(state, dispatch);
551
- }
552
- })
553
- }, 2)];
554
- }
555
- };
556
- //#endregion
557
- //#region src/lib/rich-text-editor/rte/features/internal/foreign-html.ts
558
- var RteForeignHtmlFeatureImpl = class extends require_slottable_request.RteFeatureImpl {
559
- constructor(..._args) {
560
- super(..._args);
561
- this.name = "RteForeignHtmlFeature";
562
- }
563
- getPlugins(rte) {
564
- return [this.contribution(new prosemirror_state.Plugin({ props: {
565
- transformPastedHTML: (html) => rte.foreignHtmlParser[require_slottable_request.impl].transform(html),
566
- clipboardParser: rte.foreignHtmlParser[require_slottable_request.impl].parser,
567
- clipboardSerializer: rte.foreignHtmlSerializer[require_slottable_request.impl].serializer
568
- } }))];
569
- }
570
- };
571
- //#endregion
572
- //#region src/lib/rich-text-editor/rte/features/internal/cursor-fix.ts
573
- /**
574
- * When the cursor is positioned after an inline atom node at the end of a line,
575
- * browsers may display the cursor inside the atom.
576
- */
577
- var atomCursorFix = ($cursor) => {
578
- if (!($cursor.parentOffset === $cursor.parent.content.size)) return null;
579
- const nodeBefore = $cursor.nodeBefore;
580
- if (nodeBefore && nodeBefore.isInline && nodeBefore.isAtom && !nodeBefore.isText) return {};
581
- return null;
582
- };
583
- var RteCursorFixFeatureImpl = class extends require_slottable_request.RteFeatureImpl {
584
- constructor(..._args) {
585
- super(..._args);
586
- this.name = "RteCursorFix";
587
- }
588
- getPlugins(rte) {
589
- const cursorFixes = [atomCursorFix];
590
- for (const markType of Object.values(rte.schema.marks)) {
591
- const spec = markType.spec;
592
- 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;
593
524
  }
594
- return [this.contribution(new prosemirror_state.Plugin({ props: { decorations: (state) => {
595
- const { $cursor } = state.selection;
596
- if (!$cursor) return null;
597
- let cursorFix = null;
598
- for (const fn of cursorFixes) {
599
- const result = fn($cursor, state);
600
- if (result) Object.assign(cursorFix ??= {}, result);
601
- }
602
- if (!cursorFix) return null;
603
- return prosemirror_view.DecorationSet.create(state.doc, [prosemirror_view.Decoration.widget($cursor.pos, () => {
604
- const span = document.createElement("span");
605
- span.textContent = "​";
606
- for (const [prop, value] of Object.entries(cursorFix)) span.style.setProperty(prop, value);
607
- return span;
608
- }, { side: -1 })]);
609
- } } }))];
610
- }
611
- };
612
- require_slottable_request.featureFacade(RteCursorFixFeatureImpl);
613
- //#endregion
614
- //#region src/lib/rich-text-editor/rte/features/internal/core.ts
615
- /**
616
- * Plugin to bring state from the host web component into ProseMirror.
617
- */
618
- var hostBridgePlugin = new prosemirror_state.Plugin({ state: {
619
- init() {
620
- return null;
621
- },
622
- apply(tr, value) {
623
- const meta = tr.getMeta(hostBridgePlugin);
624
- if (meta) return meta;
625
- else return value;
626
- }
627
- } });
628
- var RteCoreImpl = class extends require_slottable_request.RteFeatureImpl {
629
- constructor(..._args) {
630
- super(..._args);
631
- this.name = "RteCore";
632
- this.disabled = new FeatureState(false);
633
- }
634
- getStyles() {
635
- return [
636
- this.contribution(prosemirror_default),
637
- this.contribution(core_style_default),
638
- this.contribution(ui_style_default)
639
- ];
640
- }
641
- getPlugins(rte) {
642
- const enterKeyChainCommands = (0, prosemirror_commands.chainCommands)(prosemirror_commands.newlineInCode, prosemirror_commands.createParagraphNear, prosemirror_commands.liftEmptyBlock, (0, prosemirror_commands.splitBlockAs)((node, atEnd, $from) => {
643
- if (!atEnd) return {
644
- type: node.type,
645
- attrs: node.attrs
646
- };
647
- return {
648
- type: defaultTextblockForMatch($from.node($from.depth - 1).contentMatchAt($from.indexAfter($from.depth - 1))),
649
- attrs: rte.textblockAttrs.extractFromNode(node)
650
- };
651
- }));
652
- return [
653
- this.contribution(this.disabled.plugin),
654
- this.contribution(new prosemirror_state.Plugin({
655
- props: {
656
- editable: () => !this.disabled.getValue(rte),
657
- handleDOMEvents: { click: (_view, event) => {
658
- if (this.disabled.getValue(rte)) {
659
- event.preventDefault();
660
- return true;
661
- }
662
- return false;
663
- } }
664
- },
665
- view: (view) => {
666
- const popovers = view.dom.getRootNode().querySelector(".popovers");
667
- const updateDisabled = () => {
668
- const disabled = this.disabled.getValue(rte);
669
- popovers.classList.toggle("popovers--disabled", disabled);
670
- view.dom.parentElement.classList.toggle("editor--disabled", disabled);
671
- };
672
- updateDisabled();
673
- return { update: () => {
674
- updateDisabled();
675
- } };
676
- }
677
- })),
678
- this.contribution((0, prosemirror_keymap.keymap)({
679
- ...prosemirror_commands.baseKeymap,
680
- Enter: enterKeyChainCommands,
681
- "Shift-Enter": enterKeyChainCommands
682
- })),
683
- this.contribution((0, prosemirror_dropcursor.dropCursor)()),
684
- this.contribution((0, prosemirror_gapcursor.gapCursor)()),
685
- this.contribution(hostBridgePlugin)
686
- ];
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;
687
531
  }
688
- getFeatures() {
689
- return [
690
- this,
691
- new RteHistoryFeatureImpl(),
692
- new RteForeignHtmlFeatureImpl(),
693
- new RteCursorFixFeatureImpl()
694
- ];
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;
695
541
  }
696
542
  };
697
- require_slottable_request.featureFacade(RteCoreImpl);
698
543
  //#endregion
699
- //#region src/lib/rich-text-editor/rte/utils/sanitization.ts
700
- var DEFAULT_ALLOWED_URI_REGEXP = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
701
- var ALLOWED_URI_REGEXP_WITH_BLOB = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|blob):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
702
- var ATTR_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
703
- var domPurifyConfig = { ALLOWED_URI_REGEXP: ALLOWED_URI_REGEXP_WITH_BLOB };
704
- /**
705
- * Sanitize potentially dangerous URLs, like "javascript:", in anchor href attributes.
706
- * Returns empty string if the URL is unsafe.
707
- */
708
- var sanitizeLinkHref = (url) => {
709
- if (!DEFAULT_ALLOWED_URI_REGEXP.test(url.replace(ATTR_WHITESPACE, ""))) return "";
710
- const anchor = document.createElement("a");
711
- anchor.setAttribute("href", url);
712
- /* v8 ignore next -- since href is already validated it's probably always present @preserve */
713
- return dompurify.default.sanitize(anchor, {
714
- RETURN_DOM: true,
715
- ...domPurifyConfig
716
- }).querySelector("a").getAttribute("href") ?? "";
717
- };
718
- /**
719
- * Sanitize potentially dangerous URLs, like "javascript:", in image src attributes.
720
- * Returns empty string if the URL is unsafe.
721
- */
722
- var sanitizeImageSrc = (url) => {
723
- const img = document.createElement("img");
724
- img.setAttribute("src", url);
725
- return dompurify.default.sanitize(img, {
726
- RETURN_DOM: true,
727
- ...domPurifyConfig
728
- }).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;
729
550
  };
730
- /**
731
- * Escapes a CSS value for insertion into style attribute.
732
- * E.g. "15px; background: red" => "15px"
733
- */
734
- var escapeCssProperty = (value) => value.replace(/[;!].*/, "");
735
- //#endregion
736
- //#region src/lib/rich-text-editor/rte/html-parser.ts
737
- var copy = (obj) => ({ ...obj });
738
- var parseRulesFromSchema = (schema) => ({
739
- nodes: Object.fromEntries(Object.keys(schema.nodes).map((name) => [name, (schema.nodes[name].spec.parseDOM ?? []).map(copy)])),
740
- marks: Object.fromEntries(Object.keys(schema.marks).map((name) => [name, (schema.marks[name].spec.parseDOM ?? []).map(copy)]))
741
- });
742
- var RteHtmlParser = class {
551
+ var RteInstance = class {
743
552
  constructor(config, options) {
744
- this[require_slottable_request.impl] = new RteHtmlParserImpl(config[require_slottable_request.impl], options);
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);
745
558
  }
746
559
  /**
747
- * Converts an HTML string to an RteDocument.
560
+ * Returns the current document state.
748
561
  */
749
- parseDocument(html, options) {
750
- return this[require_slottable_request.impl].parser.parse(this.parseHtml(html, options), { preserveWhitespace: true }).toJSON();
562
+ getDocument() {
563
+ return this[require_slottable_request.impl].state.doc.toJSON();
751
564
  }
752
565
  /**
753
- * Converts an HTML string to an RteFragment.
754
- */
755
- parseFragment(html, options) {
756
- return this[require_slottable_request.impl].parser.parseSlice(this.parseHtml(html, options), { preserveWhitespace: true }).content.toJSON() ?? [];
757
- }
758
- parseHtml(html, options) {
759
- const dom = dompurify.default.sanitize(html, {
760
- RETURN_DOM: true,
761
- ...domPurifyConfig
762
- });
763
- const container = document.createDocumentFragment();
764
- container.appendChild(dom);
765
- options?.modifyDom?.(container);
766
- return container;
767
- }
768
- };
769
- var RteHtmlParserImpl = class {
770
- constructor(config, options) {
771
- const rules = parseRulesFromSchema(config.schema);
772
- options?.modifyParseRules?.(rules);
773
- this.parser = buildDomParser(config.schema, rules);
774
- }
775
- transform(html) {
776
- return dompurify.default.sanitize(html);
777
- }
778
- };
779
- var buildDomParser = (schema, { marks, nodes }) => {
780
- const parserRules = [];
781
- const priority = (rule) => rule.priority ?? 50;
782
- function insert(rule) {
783
- let i = 0;
784
- for (; i < parserRules.length; i++) {
785
- const next = parserRules[i];
786
- if (priority(next) < priority(rule)) break;
787
- }
788
- parserRules.splice(i, 0, rule);
789
- }
790
- for (const name in marks) marks[name]?.forEach((rule) => {
791
- insert(rule = copy(rule));
792
- if (!(rule.mark || rule.ignore || rule.clearMark)) rule.mark = name;
793
- });
794
- for (const name in nodes) nodes[name]?.forEach((rule) => {
795
- insert(rule = copy(rule));
796
- if (!(rule.node || rule.ignore || rule.mark)) rule.node = name;
797
- });
798
- parserRules.push({
799
- tag: "br",
800
- closeParent: true
801
- });
802
- return new prosemirror_model.DOMParser(schema, parserRules);
803
- };
804
- //#endregion
805
- //#region src/lib/rich-text-editor/rte/html-serializer.ts
806
- var RteHtmlSerializer = class {
807
- constructor(config, options) {
808
- this[require_slottable_request.impl] = new RteHtmlSerializerImpl(config[require_slottable_request.impl], options);
809
- }
810
- /**
811
- * Converts an RteDocument to an HTML string.
812
- */
813
- serializeDocument(doc, options) {
814
- return this[require_slottable_request.impl].serializeFragment(doc.content, options);
815
- }
816
- /**
817
- * Converts an RteFragment to an HTML string.
818
- */
819
- serializeFragment(fragment, options) {
820
- return this[require_slottable_request.impl].serializeFragment(fragment, options);
821
- }
822
- };
823
- var RteHtmlSerializerImpl = class RteHtmlSerializerImpl {
824
- constructor(config, options) {
825
- this.config = config;
826
- const serializers = RteHtmlSerializerImpl.domSerializersFromSchema(config.schema);
827
- Object.assign(serializers.nodes, options?.serializers?.nodes ?? {});
828
- Object.assign(serializers.marks, options?.serializers?.marks ?? {});
829
- this.serializer = new prosemirror_model.DOMSerializer(serializers.nodes, serializers.marks);
830
- }
831
- static domSerializersFromSchema(schema) {
832
- const result = {
833
- nodes: {},
834
- marks: {}
835
- };
836
- for (const name in schema.marks) {
837
- const toDOM = schema.marks[name].spec.toDOM;
838
- if (toDOM) result.marks[name] = toDOM;
839
- }
840
- for (const name in schema.nodes) {
841
- const toDOM = schema.nodes[name].spec.serializeToDOM ?? schema.nodes[name].spec.toDOM;
842
- if (toDOM) result.nodes[name] = toDOM;
843
- }
844
- result.nodes.text = (node) => document.createTextNode(node.text);
845
- return result;
846
- }
847
- serializeFragment(fragment, options) {
848
- const parsedFragment = prosemirror_model.Fragment.fromJSON(this.config.schema, fragment);
849
- const serializedFragment = this.serializer.serializeFragment(parsedFragment);
850
- const container = document.createDocumentFragment();
851
- container.appendChild(serializedFragment);
852
- options?.modifyDom?.(container);
853
- const output = document.createElement("div");
854
- output.appendChild(container);
855
- return output.innerHTML;
856
- }
857
- };
858
- //#endregion
859
- //#region src/lib/rich-text-editor/rte/instance.ts
860
- var parseDocument = (schema, doc) => {
861
- const node = schema.topNodeType.createAndFill(null, doc ? prosemirror_model.Fragment.fromJSON(schema, doc.content) : null);
862
- if (!node) throw new Error("Document could not be parsed");
863
- node.check();
864
- return node;
865
- };
866
- var RteInstance = class {
867
- constructor(config, options) {
868
- this.options = options;
869
- this.feature = (Feature, featureId) => {
870
- return this[require_slottable_request.impl].getPublicInterface(Feature, featureId);
871
- };
872
- this[require_slottable_request.impl] = new RteInstanceImpl(this, config, options);
873
- }
874
- /**
875
- * Returns the current document state.
876
- */
877
- getDocument() {
878
- return this[require_slottable_request.impl].state.doc.toJSON();
879
- }
880
- /**
881
- * Reset the editor to its initial state. Optionally, an initial document can be provided.
566
+ * Reset the editor to its initial state. Optionally, an initial document can be provided.
882
567
  */
883
568
  reset(initialDocument) {
884
569
  this[require_slottable_request.impl].reset(initialDocument);
@@ -1164,6 +849,322 @@ var RteConfigImpl = class {
1164
849
  }
1165
850
  };
1166
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;
1078
+ };
1079
+ var createText = (ctx, props) => {
1080
+ const textNode = document.createTextNode("");
1081
+ ctx.bindToEl(textNode, { textContent: props.text });
1082
+ return textNode;
1083
+ };
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
1109
+ });
1110
+ }
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
+ }
1120
+ };
1121
+ slot.addEventListener("slotchange", processSlot);
1122
+ queueMicrotask(processSlot);
1123
+ return slot;
1124
+ };
1125
+ //#endregion
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";
1132
+ }
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
+ }))];
1139
+ }
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)];
1165
+ }
1166
+ };
1167
+ //#endregion
1167
1168
  //#region src/lib/rich-text-editor/rte/features/internal/basic-text-blocks.style.scss?inline
1168
1169
  var basic_text_blocks_style_default = "h1,h2,h3,p{all:unset;display:block}h1:first-child,h2:first-child,h3:first-child,p:first-child{margin-block-start:0}h1:last-child,h2:last-child,h3:last-child,p:last-child{margin-block-end:0}p{font:var(--vvd-typography-base);font-variant-ligatures:unset;font-feature-settings:unset;margin-block:16px;line-height:20px}.heading-step-1{font:var(--vvd-typography-heading-4);font-variant-ligatures:unset;font-feature-settings:unset;margin-block:24px 12px;line-height:28px}.heading-step-2{font:var(--vvd-typography-heading-3);font-variant-ligatures:unset;font-feature-settings:unset;margin-block:24px 12px;line-height:36px}.heading-step-3{font:var(--vvd-typography-heading-2);font-variant-ligatures:unset;font-feature-settings:unset;margin-block:32px 16px;line-height:42px}";
1169
1170
  //#endregion
@@ -1346,6 +1347,7 @@ var RteBaseImpl = class extends require_slottable_request.RteFeatureImpl {
1346
1347
  return [
1347
1348
  this,
1348
1349
  new RteCoreImpl(),
1350
+ new RteHistoryFeatureImpl({ showToolbarButtons: this.config?.historyToolbarButtons ?? true }),
1349
1351
  new RteBasicTextBlocksImpl({
1350
1352
  heading1: this.config?.heading1 ?? false,
1351
1353
  heading2: this.config?.heading2 ?? false,
@@ -3225,7 +3227,6 @@ var RteInputRuleFeature = require_slottable_request.featureFacade(RteInputRuleFe
3225
3227
  //#endregion
3226
3228
  //#region src/lib/rich-text-editor/rte/features/keyboard-shortcuts.ts
3227
3229
  function toCommand(handler, rteInstance) {
3228
- if (handler.length === 0) return (_state, _dispatch) => handler();
3229
3230
  return (_state, _dispatch) => handler(rteInstance);
3230
3231
  }
3231
3232
  var RteKeyboardShortcutsFeatureImpl = class extends require_slottable_request.RteFeatureImpl {