@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
@@ -22,8 +22,8 @@ import { keymap } from "prosemirror-keymap";
22
22
  import { autoJoin, baseKeymap, chainCommands, createParagraphNear, liftEmptyBlock, newlineInCode, splitBlockAs, toggleMark } from "prosemirror-commands";
23
23
  import { dropCursor } from "prosemirror-dropcursor";
24
24
  import { gapCursor } from "prosemirror-gapcursor";
25
- import { closeHistory, history, redo, undo } from "prosemirror-history";
26
25
  import DOMPurify from "dompurify";
26
+ import { closeHistory, history, redo, undo } from "prosemirror-history";
27
27
  import { InputRule, inputRules, undoInputRule } from "prosemirror-inputrules";
28
28
  import { RemoveMarkStep, ReplaceAroundStep, dropPoint } from "prosemirror-transform";
29
29
  import { marks } from "prosemirror-schema-basic";
@@ -238,645 +238,330 @@ var FeatureState = class {
238
238
  //#region src/lib/rich-text-editor/rte/features/internal/core.style.scss?inline
239
239
  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}";
240
240
  //#endregion
241
- //#region src/lib/rich-text-editor/rte/utils/ui.ts
242
- var isPropBinding = (prop) => typeof prop === "function";
243
- var on = (event, prop, handler) => [
244
- event,
245
- prop,
246
- handler
247
- ];
248
- var UiCtx = class {
249
- constructor(view, rte, props) {
250
- this.view = view;
251
- this.rte = rte;
252
- this.props = props;
253
- this.bindings = [];
241
+ //#region src/lib/rich-text-editor/rte/features/internal/foreign-html.ts
242
+ var RteForeignHtmlFeatureImpl = class extends RteFeatureImpl {
243
+ constructor(..._args) {
244
+ super(..._args);
245
+ this.name = "RteForeignHtmlFeature";
254
246
  }
255
- evalProp(prop) {
256
- return isPropBinding(prop) ? prop(this) : prop;
247
+ getPlugins(rte) {
248
+ return [this.contribution(new Plugin({ props: {
249
+ transformPastedHTML: (html) => rte.foreignHtmlParser[impl].transform(html),
250
+ clipboardParser: rte.foreignHtmlParser[impl].parser,
251
+ clipboardSerializer: rte.foreignHtmlSerializer[impl].serializer
252
+ } }))];
257
253
  }
258
- bindProp(prop, bindFn) {
259
- if (prop === void 0) return;
260
- else if (isPropBinding(prop)) {
261
- const binding = () => bindFn(prop(this));
262
- this.bindings.push(binding);
263
- binding();
264
- } else bindFn(prop);
254
+ };
255
+ //#endregion
256
+ //#region src/lib/rich-text-editor/rte/features/internal/cursor-fix.ts
257
+ /**
258
+ * When the cursor is positioned after an inline atom node at the end of a line,
259
+ * browsers may display the cursor inside the atom.
260
+ */
261
+ var atomCursorFix = ($cursor) => {
262
+ if (!($cursor.parentOffset === $cursor.parent.content.size)) return null;
263
+ const nodeBefore = $cursor.nodeBefore;
264
+ if (nodeBefore && nodeBefore.isInline && nodeBefore.isAtom && !nodeBefore.isText) return {};
265
+ return null;
266
+ };
267
+ var RteCursorFixFeatureImpl = class extends RteFeatureImpl {
268
+ constructor(..._args) {
269
+ super(..._args);
270
+ this.name = "RteCursorFix";
265
271
  }
266
- updateBindings() {
267
- for (const binding of this.bindings) binding();
272
+ getPlugins(rte) {
273
+ const cursorFixes = [atomCursorFix];
274
+ for (const markType of Object.values(rte.schema.marks)) {
275
+ const spec = markType.spec;
276
+ if (spec.cursorFix) cursorFixes.push(spec.cursorFix);
277
+ }
278
+ return [this.contribution(new Plugin({ props: { decorations: (state) => {
279
+ const { $cursor } = state.selection;
280
+ if (!$cursor) return null;
281
+ let cursorFix = null;
282
+ for (const fn of cursorFixes) {
283
+ const result = fn($cursor, state);
284
+ if (result) Object.assign(cursorFix ??= {}, result);
285
+ }
286
+ if (!cursorFix) return null;
287
+ return DecorationSet.create(state.doc, [Decoration.widget($cursor.pos, () => {
288
+ const span = document.createElement("span");
289
+ span.textContent = "​";
290
+ for (const [prop, value] of Object.entries(cursorFix)) span.style.setProperty(prop, value);
291
+ return span;
292
+ }, { side: -1 })]);
293
+ } } }))];
268
294
  }
269
- shouldReturnFocusToEditor() {
270
- return this.evalProp(this.props.shouldReturnFocusToEditor);
295
+ };
296
+ featureFacade(RteCursorFixFeatureImpl);
297
+ //#endregion
298
+ //#region src/lib/rich-text-editor/rte/features/internal/core.ts
299
+ /**
300
+ * Plugin to bring state from the host web component into ProseMirror.
301
+ */
302
+ var hostBridgePlugin = new Plugin({ state: {
303
+ init() {
304
+ return null;
305
+ },
306
+ apply(tr, value) {
307
+ const meta = tr.getMeta(hostBridgePlugin);
308
+ if (meta) return meta;
309
+ else return value;
271
310
  }
272
- bindToEl(target, props = {}, events = [], children = []) {
273
- for (const name in props) this.bindProp(props[name], (value) => {
274
- target[name] = value;
275
- });
276
- for (const [name, value, bindFn] of events) if (value) target.addEventListener(name, bindFn(value));
277
- for (const child of children) target.appendChild(child);
311
+ } });
312
+ var RteCoreImpl = class extends RteFeatureImpl {
313
+ constructor(..._args) {
314
+ super(..._args);
315
+ this.name = "RteCore";
316
+ this.disabled = new FeatureState(false);
317
+ }
318
+ getStyles() {
319
+ return [
320
+ this.contribution(prosemirror_default),
321
+ this.contribution(core_style_default),
322
+ this.contribution(ui_style_default)
323
+ ];
324
+ }
325
+ getPlugins(rte) {
326
+ const enterKeyChainCommands = chainCommands(newlineInCode, createParagraphNear, liftEmptyBlock, splitBlockAs((node, atEnd, $from) => {
327
+ if (!atEnd) return {
328
+ type: node.type,
329
+ attrs: node.attrs
330
+ };
331
+ return {
332
+ type: defaultTextblockForMatch($from.node($from.depth - 1).contentMatchAt($from.indexAfter($from.depth - 1))),
333
+ attrs: rte.textblockAttrs.extractFromNode(node)
334
+ };
335
+ }));
336
+ return [
337
+ this.contribution(this.disabled.plugin),
338
+ this.contribution(new Plugin({
339
+ props: {
340
+ editable: () => !this.disabled.getValue(rte),
341
+ handleDOMEvents: { click: (_view, event) => {
342
+ if (this.disabled.getValue(rte)) {
343
+ event.preventDefault();
344
+ return true;
345
+ }
346
+ return false;
347
+ } }
348
+ },
349
+ view: (view) => {
350
+ const popovers = view.dom.getRootNode().querySelector(".popovers");
351
+ const updateDisabled = () => {
352
+ const disabled = this.disabled.getValue(rte);
353
+ popovers.classList.toggle("popovers--disabled", disabled);
354
+ view.dom.parentElement.classList.toggle("editor--disabled", disabled);
355
+ };
356
+ updateDisabled();
357
+ return { update: () => {
358
+ updateDisabled();
359
+ } };
360
+ }
361
+ })),
362
+ this.contribution(keymap({
363
+ ...baseKeymap,
364
+ Enter: enterKeyChainCommands,
365
+ "Shift-Enter": enterKeyChainCommands
366
+ })),
367
+ this.contribution(dropCursor()),
368
+ this.contribution(gapCursor()),
369
+ this.contribution(hostBridgePlugin)
370
+ ];
371
+ }
372
+ getFeatures() {
373
+ return [
374
+ this,
375
+ new RteForeignHtmlFeatureImpl(),
376
+ new RteCursorFixFeatureImpl()
377
+ ];
278
378
  }
279
379
  };
280
- var createDiv = (ctx, props) => {
281
- const div = document.createElement("div");
282
- ctx.bindToEl(div, {
283
- className: props.className,
284
- slot: props.slot
285
- }, [], props.children);
286
- return div;
287
- };
288
- var createAnchor = (ctx, props) => {
380
+ featureFacade(RteCoreImpl);
381
+ //#endregion
382
+ //#region src/lib/rich-text-editor/rte/utils/sanitization.ts
383
+ var DEFAULT_ALLOWED_URI_REGEXP = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
384
+ var ALLOWED_URI_REGEXP_WITH_BLOB = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|blob):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
385
+ var ATTR_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
386
+ var domPurifyConfig = { ALLOWED_URI_REGEXP: ALLOWED_URI_REGEXP_WITH_BLOB };
387
+ /**
388
+ * Sanitize potentially dangerous URLs, like "javascript:", in anchor href attributes.
389
+ * Returns empty string if the URL is unsafe.
390
+ */
391
+ var sanitizeLinkHref = (url) => {
392
+ if (!DEFAULT_ALLOWED_URI_REGEXP.test(url.replace(ATTR_WHITESPACE, ""))) return "";
289
393
  const anchor = document.createElement("a");
290
- ctx.bindToEl(anchor, {
291
- href: props.href,
292
- target: props.target,
293
- rel: props.rel,
294
- className: props.className
295
- }, [], props.children);
296
- return anchor;
394
+ anchor.setAttribute("href", url);
395
+ /* v8 ignore next -- since href is already validated it's probably always present @preserve */
396
+ return DOMPurify.sanitize(anchor, {
397
+ RETURN_DOM: true,
398
+ ...domPurifyConfig
399
+ }).querySelector("a").getAttribute("href") ?? "";
297
400
  };
298
- var wrapperTargets = /* @__PURE__ */ new WeakMap();
299
- var createOptionalTooltip = (ctx, props) => {
300
- const tooltip = ctx.rte.createComponent(Tooltip);
301
- tooltip.setAttribute("exportparts", "vvd-theme-alternate");
302
- ctx.bindToEl(tooltip, {
303
- className: "ui-tooltip",
304
- text: props.label,
305
- placement: ctx.props.popupPlacement
306
- });
307
- ctx.bindProp(props.enabled, (enabled) => {
308
- if (!enabled) tooltip.open = false;
309
- tooltip.anchor = enabled ? props.anchor : void 0;
310
- });
311
- const wrapper = createDiv(ctx, {
312
- className: "ui-tooltip-wrapper",
313
- slot: props.slot,
314
- children: [props.anchor, tooltip]
315
- });
316
- wrapperTargets.set(wrapper, props.anchor);
317
- return wrapper;
401
+ /**
402
+ * Sanitize potentially dangerous URLs, like "javascript:", in image src attributes.
403
+ * Returns empty string if the URL is unsafe.
404
+ */
405
+ var sanitizeImageSrc = (url) => {
406
+ const img = document.createElement("img");
407
+ img.setAttribute("src", url);
408
+ return DOMPurify.sanitize(img, {
409
+ RETURN_DOM: true,
410
+ ...domPurifyConfig
411
+ }).querySelector("img").getAttribute("src") ?? "";
318
412
  };
319
- var createButton = (ctx, props) => {
320
- const variant = () => ctx.evalProp(props.variant) ?? "toolbar";
321
- const size = () => variant() === "toolbar" ? "super-condensed" : "condensed";
322
- const appearanceInactive = () => variant() === "popover-primary" ? "outlined" : "ghost-light";
323
- const appearanceActive = () => variant() === "toolbar-menu" || variant() === "popover" ? "filled" : appearanceInactive();
324
- const appearance = () => ctx.evalProp(props.active) ? appearanceActive() : appearanceInactive();
325
- const connotation = () => ctx.evalProp(props.connotation) ?? (variant() === "toolbar-menu" && ctx.evalProp(props.active) ? "cta" : void 0);
326
- const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled) || ctx.evalProp(props.disabled));
327
- const button = ctx.rte.createComponent(Button);
328
- ctx.bindToEl(button, {
329
- size,
330
- appearance,
331
- disabled,
332
- icon: props.icon,
333
- autofocus: props.autofocus,
334
- connotation,
335
- [props.icon ? "ariaLabel" : "label"]: props.label,
336
- pressed: () => Boolean(ctx.evalProp(props.active)),
337
- ariaPressed: props.active !== void 0 ? () => ctx.evalProp(props.active) ? "true" : "false" : void 0
338
- }, [on("click", props.onClick, (onClick) => () => {
339
- if (!onClick() && ctx.shouldReturnFocusToEditor()) ctx.view.focus();
340
- })]);
341
- return createOptionalTooltip(ctx, {
342
- enabled: () => Boolean(ctx.evalProp(props.icon) && !ctx.evalProp(props.noTooltip) && !disabled()),
343
- label: props.label,
344
- anchor: button,
345
- slot: props.slot
346
- });
347
- };
348
- var createMenu = (ctx, props) => {
349
- const menu = ctx.rte.createComponent(Menu);
350
- ctx.bindToEl(menu, {
351
- className: "ui-menu",
352
- autoDismiss: true,
353
- ariaLabel: props.label,
354
- anchor: wrapperTargets.get(props.trigger) ?? props.trigger,
355
- placement: ctx.props.popupPlacement,
356
- offset: () => ctx.evalProp(ctx.props.menuOffset) ?? null
357
- }, [], props.children);
358
- const fragment = document.createDocumentFragment();
359
- fragment.appendChild(props.trigger);
360
- fragment.appendChild(menu);
361
- return fragment;
362
- };
363
- var createMenuItem = (ctx, props) => {
364
- const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled) || ctx.evalProp(props.disabled));
365
- const item = ctx.rte.createComponent(MenuItem);
366
- ctx.bindToEl(item, {
367
- text: props.text,
368
- checked: props.checked,
369
- disabled,
370
- controlType: "radio",
371
- checkedAppearance: "tick-only"
372
- }, [on("change", props.onSelect, (onSelect) => () => {
373
- if (item.checked && !item.disabled) {
374
- if (ctx.evalProp(props.checked) !== item.checked) {
375
- onSelect();
376
- if (ctx.shouldReturnFocusToEditor()) ctx.view.focus();
377
- }
378
- }
379
- })]);
380
- return item;
381
- };
382
- var createButtonGroup = (ctx, props) => createDiv(ctx, {
383
- slot: props.slot,
384
- className: "ui-button-group",
385
- children: props.children
413
+ /**
414
+ * Escapes a CSS value for insertion into style attribute.
415
+ * E.g. "15px; background: red" => "15px"
416
+ */
417
+ var escapeCssProperty = (value) => value.replace(/[;!].*/, "");
418
+ //#endregion
419
+ //#region src/lib/rich-text-editor/rte/html-parser.ts
420
+ var copy = (obj) => ({ ...obj });
421
+ var parseRulesFromSchema = (schema) => ({
422
+ nodes: Object.fromEntries(Object.keys(schema.nodes).map((name) => [name, (schema.nodes[name].spec.parseDOM ?? []).map(copy)])),
423
+ marks: Object.fromEntries(Object.keys(schema.marks).map((name) => [name, (schema.marks[name].spec.parseDOM ?? []).map(copy)]))
386
424
  });
387
- function markActive(state, type) {
388
- const { from, $from, to, empty } = state.selection;
389
- if (empty) return !!type.isInSet(state.storedMarks || $from.marks());
390
- else return state.doc.rangeHasMark(from, to, type);
391
- }
392
- var createMarkToggle = (ctx, props) => createButton(ctx, {
393
- label: props.label,
394
- icon: props.icon,
395
- active: () => markActive(ctx.view.state, props.markType),
396
- disabled: () => !toggleMark(props.markType)(ctx.view.state),
397
- onClick: () => {
398
- toggleMark(props.markType)(ctx.view.state, ctx.view.dispatch);
425
+ var RteHtmlParser = class {
426
+ constructor(config, options) {
427
+ this[impl] = new RteHtmlParserImpl(config[impl], options);
428
+ }
429
+ /**
430
+ * Converts an HTML string to an RteDocument.
431
+ */
432
+ parseDocument(html, options) {
433
+ return this[impl].parser.parse(this.parseHtml(html, options), { preserveWhitespace: true }).toJSON();
434
+ }
435
+ /**
436
+ * Converts an HTML string to an RteFragment.
437
+ */
438
+ parseFragment(html, options) {
439
+ return this[impl].parser.parseSlice(this.parseHtml(html, options), { preserveWhitespace: true }).content.toJSON() ?? [];
440
+ }
441
+ parseHtml(html, options) {
442
+ const dom = DOMPurify.sanitize(html, {
443
+ RETURN_DOM: true,
444
+ ...domPurifyConfig
445
+ });
446
+ const container = document.createDocumentFragment();
447
+ container.appendChild(dom);
448
+ options?.modifyDom?.(container);
449
+ return container;
399
450
  }
400
- });
401
- var createOption = (ctx, props) => {
402
- const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled) || ctx.evalProp(props.disabled));
403
- const option = ctx.rte.createComponent(ListboxOption);
404
- ctx.bindToEl(option, {
405
- value: props.value,
406
- text: props.text,
407
- disabled
408
- });
409
- return option;
410
451
  };
411
- var createSelect = (ctx, props) => {
412
- const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled));
413
- const select = ctx.rte.createComponent(Select);
414
- select.setAttribute("data-class", "ui-select");
415
- ctx.bindToEl(select, {
416
- placeholder: " ",
417
- ariaLabel: props.label,
418
- appearance: "ghost",
419
- disabled
420
- }, [on("change", props.onSelect, (onSelect) => () => {
421
- const value = select.value;
422
- if (value) {
423
- onSelect(value);
424
- if (ctx.shouldReturnFocusToEditor()) ctx.view.focus();
452
+ var RteHtmlParserImpl = class {
453
+ constructor(config, options) {
454
+ const rules = parseRulesFromSchema(config.schema);
455
+ options?.modifyParseRules?.(rules);
456
+ this.parser = buildDomParser(config.schema, rules);
457
+ }
458
+ transform(html) {
459
+ return DOMPurify.sanitize(html);
460
+ }
461
+ };
462
+ var buildDomParser = (schema, { marks, nodes }) => {
463
+ const parserRules = [];
464
+ const priority = (rule) => rule.priority ?? 50;
465
+ function insert(rule) {
466
+ let i = 0;
467
+ for (; i < parserRules.length; i++) {
468
+ const next = parserRules[i];
469
+ if (priority(next) < priority(rule)) break;
425
470
  }
426
- })], props.children);
427
- queueMicrotask(() => {
428
- ctx.bindProp(props.value, (value) => select.value = value);
429
- });
430
- let hideTooltip = false;
431
- select.addEventListener("vwc-popup:open", () => {
432
- hideTooltip = true;
433
- ctx.updateBindings();
434
- });
435
- select.addEventListener("vwc-popup:close", () => {
436
- hideTooltip = false;
437
- ctx.updateBindings();
471
+ parserRules.splice(i, 0, rule);
472
+ }
473
+ for (const name in marks) marks[name]?.forEach((rule) => {
474
+ insert(rule = copy(rule));
475
+ if (!(rule.mark || rule.ignore || rule.clearMark)) rule.mark = name;
438
476
  });
439
- return createOptionalTooltip(ctx, {
440
- enabled: () => !hideTooltip && !disabled(),
441
- label: props.label,
442
- anchor: select
477
+ for (const name in nodes) nodes[name]?.forEach((rule) => {
478
+ insert(rule = copy(rule));
479
+ if (!(rule.node || rule.ignore || rule.mark)) rule.node = name;
443
480
  });
444
- };
445
- var createDivider = (ctx) => {
446
- const divider = ctx.rte.createComponent(Divider);
447
- ctx.bindToEl(divider, {
448
- className: "ui-divider",
449
- orientation: "vertical"
481
+ parserRules.push({
482
+ tag: "br",
483
+ closeParent: true
450
484
  });
451
- return divider;
452
- };
453
- var createTextField = (ctx, props) => {
454
- const textField = ctx.rte.createComponent(TextField);
455
- ctx.bindToEl(textField, {
456
- label: props.label,
457
- value: props.value,
458
- placeholder: props.placeholder,
459
- slot: props.slot,
460
- autofocus: props.autofocus,
461
- type: props.type,
462
- helperText: props.helperText
463
- }, [on("input", props.onInput, (onInput) => () => {
464
- onInput(textField.value);
465
- })]);
466
- return textField;
467
- };
468
- var createText = (ctx, props) => {
469
- const textNode = document.createTextNode("");
470
- ctx.bindToEl(textNode, { textContent: props.text });
471
- return textNode;
485
+ return new DOMParser(schema, parserRules);
472
486
  };
473
- var createSingleSlot = (ctx, props) => {
474
- const slot = document.createElement("slot");
475
- slot.name = props.name;
476
- let currentEl = null;
477
- const listeners = [];
478
- function cleanup() {
479
- for (const { el, type, handler } of listeners) el.removeEventListener(type, handler);
480
- listeners.length = 0;
481
- currentEl = null;
487
+ //#endregion
488
+ //#region src/lib/rich-text-editor/rte/html-serializer.ts
489
+ var RteHtmlSerializer = class {
490
+ constructor(config, options) {
491
+ this[impl] = new RteHtmlSerializerImpl(config[impl], options);
482
492
  }
483
- for (const [key, prop] of Object.entries(props.assignedProps)) ctx.bindProp(prop, (value) => {
484
- if (currentEl) currentEl[key] = value;
485
- });
486
- function applyPropsAndEvents(el) {
487
- for (const [key, prop] of Object.entries(props.assignedProps)) el[key] = ctx.evalProp(prop);
488
- for (const [type, handler] of Object.entries(props.assignedEvents)) {
489
- const listener = (e) => {
490
- handler(e);
491
- if (ctx.shouldReturnFocusToEditor()) ctx.view.focus();
492
- };
493
- el.addEventListener(type, listener);
494
- listeners.push({
495
- el,
496
- type,
497
- handler: listener
498
- });
499
- }
493
+ /**
494
+ * Converts an RteDocument to an HTML string.
495
+ */
496
+ serializeDocument(doc, options) {
497
+ return this[impl].serializeFragment(doc.content, options);
498
+ }
499
+ /**
500
+ * Converts an RteFragment to an HTML string.
501
+ */
502
+ serializeFragment(fragment, options) {
503
+ return this[impl].serializeFragment(fragment, options);
500
504
  }
501
- const processSlot = () => {
502
- const first = slot.assignedElements({ flatten: true })[0];
503
- if (first === currentEl) return;
504
- cleanup();
505
- if (first) {
506
- currentEl = first;
507
- applyPropsAndEvents(first);
508
- }
509
- };
510
- slot.addEventListener("slotchange", processSlot);
511
- queueMicrotask(processSlot);
512
- return slot;
513
505
  };
514
- //#endregion
515
- //#region src/lib/rich-text-editor/rte/features/internal/history.ts
516
- var RteHistoryFeatureImpl = class extends RteFeatureImpl {
517
- constructor(..._args) {
518
- super(..._args);
519
- this.name = "RteHistoryFeature";
506
+ var RteHtmlSerializerImpl = class RteHtmlSerializerImpl {
507
+ constructor(config, options) {
508
+ this.config = config;
509
+ const serializers = RteHtmlSerializerImpl.domSerializersFromSchema(config.schema);
510
+ Object.assign(serializers.nodes, options?.serializers?.nodes ?? {});
511
+ Object.assign(serializers.marks, options?.serializers?.marks ?? {});
512
+ this.serializer = new DOMSerializer(serializers.nodes, serializers.marks);
520
513
  }
521
- getPlugins() {
522
- return [this.contribution(history()), this.contribution(keymap({
523
- "Mod-z": undo,
524
- "Ctrl-y": redo,
525
- "Cmd-Z": redo
526
- }))];
527
- }
528
- getToolbarItems() {
529
- return [this.contribution({
530
- section: "history",
531
- render: (ctx) => createButton(ctx, {
532
- label: (ctx) => ctx.rte.getLocale().richTextEditor.undo,
533
- icon: "undo-line",
534
- disabled: (ctx) => !undo(ctx.view.state),
535
- onClick: () => {
536
- const { state, dispatch } = ctx.view;
537
- undo(state, dispatch);
538
- }
539
- })
540
- }, 1), this.contribution({
541
- section: "history",
542
- render: (ctx) => createButton(ctx, {
543
- label: (ctx) => ctx.rte.getLocale().richTextEditor.redo,
544
- icon: "redo-line",
545
- disabled: (ctx) => !redo(ctx.view.state),
546
- onClick: () => {
547
- const { state, dispatch } = ctx.view;
548
- redo(state, dispatch);
549
- }
550
- })
551
- }, 2)];
552
- }
553
- };
554
- //#endregion
555
- //#region src/lib/rich-text-editor/rte/features/internal/foreign-html.ts
556
- var RteForeignHtmlFeatureImpl = class extends RteFeatureImpl {
557
- constructor(..._args) {
558
- super(..._args);
559
- this.name = "RteForeignHtmlFeature";
560
- }
561
- getPlugins(rte) {
562
- return [this.contribution(new Plugin({ props: {
563
- transformPastedHTML: (html) => rte.foreignHtmlParser[impl].transform(html),
564
- clipboardParser: rte.foreignHtmlParser[impl].parser,
565
- clipboardSerializer: rte.foreignHtmlSerializer[impl].serializer
566
- } }))];
567
- }
568
- };
569
- //#endregion
570
- //#region src/lib/rich-text-editor/rte/features/internal/cursor-fix.ts
571
- /**
572
- * When the cursor is positioned after an inline atom node at the end of a line,
573
- * browsers may display the cursor inside the atom.
574
- */
575
- var atomCursorFix = ($cursor) => {
576
- if (!($cursor.parentOffset === $cursor.parent.content.size)) return null;
577
- const nodeBefore = $cursor.nodeBefore;
578
- if (nodeBefore && nodeBefore.isInline && nodeBefore.isAtom && !nodeBefore.isText) return {};
579
- return null;
580
- };
581
- var RteCursorFixFeatureImpl = class extends RteFeatureImpl {
582
- constructor(..._args) {
583
- super(..._args);
584
- this.name = "RteCursorFix";
585
- }
586
- getPlugins(rte) {
587
- const cursorFixes = [atomCursorFix];
588
- for (const markType of Object.values(rte.schema.marks)) {
589
- const spec = markType.spec;
590
- if (spec.cursorFix) cursorFixes.push(spec.cursorFix);
514
+ static domSerializersFromSchema(schema) {
515
+ const result = {
516
+ nodes: {},
517
+ marks: {}
518
+ };
519
+ for (const name in schema.marks) {
520
+ const toDOM = schema.marks[name].spec.toDOM;
521
+ if (toDOM) result.marks[name] = toDOM;
591
522
  }
592
- return [this.contribution(new Plugin({ props: { decorations: (state) => {
593
- const { $cursor } = state.selection;
594
- if (!$cursor) return null;
595
- let cursorFix = null;
596
- for (const fn of cursorFixes) {
597
- const result = fn($cursor, state);
598
- if (result) Object.assign(cursorFix ??= {}, result);
599
- }
600
- if (!cursorFix) return null;
601
- return DecorationSet.create(state.doc, [Decoration.widget($cursor.pos, () => {
602
- const span = document.createElement("span");
603
- span.textContent = "​";
604
- for (const [prop, value] of Object.entries(cursorFix)) span.style.setProperty(prop, value);
605
- return span;
606
- }, { side: -1 })]);
607
- } } }))];
608
- }
609
- };
610
- featureFacade(RteCursorFixFeatureImpl);
611
- //#endregion
612
- //#region src/lib/rich-text-editor/rte/features/internal/core.ts
613
- /**
614
- * Plugin to bring state from the host web component into ProseMirror.
615
- */
616
- var hostBridgePlugin = new Plugin({ state: {
617
- init() {
618
- return null;
619
- },
620
- apply(tr, value) {
621
- const meta = tr.getMeta(hostBridgePlugin);
622
- if (meta) return meta;
623
- else return value;
624
- }
625
- } });
626
- var RteCoreImpl = class extends RteFeatureImpl {
627
- constructor(..._args) {
628
- super(..._args);
629
- this.name = "RteCore";
630
- this.disabled = new FeatureState(false);
631
- }
632
- getStyles() {
633
- return [
634
- this.contribution(prosemirror_default),
635
- this.contribution(core_style_default),
636
- this.contribution(ui_style_default)
637
- ];
638
- }
639
- getPlugins(rte) {
640
- const enterKeyChainCommands = chainCommands(newlineInCode, createParagraphNear, liftEmptyBlock, splitBlockAs((node, atEnd, $from) => {
641
- if (!atEnd) return {
642
- type: node.type,
643
- attrs: node.attrs
644
- };
645
- return {
646
- type: defaultTextblockForMatch($from.node($from.depth - 1).contentMatchAt($from.indexAfter($from.depth - 1))),
647
- attrs: rte.textblockAttrs.extractFromNode(node)
648
- };
649
- }));
650
- return [
651
- this.contribution(this.disabled.plugin),
652
- this.contribution(new Plugin({
653
- props: {
654
- editable: () => !this.disabled.getValue(rte),
655
- handleDOMEvents: { click: (_view, event) => {
656
- if (this.disabled.getValue(rte)) {
657
- event.preventDefault();
658
- return true;
659
- }
660
- return false;
661
- } }
662
- },
663
- view: (view) => {
664
- const popovers = view.dom.getRootNode().querySelector(".popovers");
665
- const updateDisabled = () => {
666
- const disabled = this.disabled.getValue(rte);
667
- popovers.classList.toggle("popovers--disabled", disabled);
668
- view.dom.parentElement.classList.toggle("editor--disabled", disabled);
669
- };
670
- updateDisabled();
671
- return { update: () => {
672
- updateDisabled();
673
- } };
674
- }
675
- })),
676
- this.contribution(keymap({
677
- ...baseKeymap,
678
- Enter: enterKeyChainCommands,
679
- "Shift-Enter": enterKeyChainCommands
680
- })),
681
- this.contribution(dropCursor()),
682
- this.contribution(gapCursor()),
683
- this.contribution(hostBridgePlugin)
684
- ];
523
+ for (const name in schema.nodes) {
524
+ const toDOM = schema.nodes[name].spec.serializeToDOM ?? schema.nodes[name].spec.toDOM;
525
+ if (toDOM) result.nodes[name] = toDOM;
526
+ }
527
+ result.nodes.text = (node) => document.createTextNode(node.text);
528
+ return result;
685
529
  }
686
- getFeatures() {
687
- return [
688
- this,
689
- new RteHistoryFeatureImpl(),
690
- new RteForeignHtmlFeatureImpl(),
691
- new RteCursorFixFeatureImpl()
692
- ];
530
+ serializeFragment(fragment, options) {
531
+ const parsedFragment = Fragment.fromJSON(this.config.schema, fragment);
532
+ const serializedFragment = this.serializer.serializeFragment(parsedFragment);
533
+ const container = document.createDocumentFragment();
534
+ container.appendChild(serializedFragment);
535
+ options?.modifyDom?.(container);
536
+ const output = document.createElement("div");
537
+ output.appendChild(container);
538
+ return output.innerHTML;
693
539
  }
694
540
  };
695
- featureFacade(RteCoreImpl);
696
541
  //#endregion
697
- //#region src/lib/rich-text-editor/rte/utils/sanitization.ts
698
- var DEFAULT_ALLOWED_URI_REGEXP = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
699
- var ALLOWED_URI_REGEXP_WITH_BLOB = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|blob):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
700
- var ATTR_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
701
- var domPurifyConfig = { ALLOWED_URI_REGEXP: ALLOWED_URI_REGEXP_WITH_BLOB };
702
- /**
703
- * Sanitize potentially dangerous URLs, like "javascript:", in anchor href attributes.
704
- * Returns empty string if the URL is unsafe.
705
- */
706
- var sanitizeLinkHref = (url) => {
707
- if (!DEFAULT_ALLOWED_URI_REGEXP.test(url.replace(ATTR_WHITESPACE, ""))) return "";
708
- const anchor = document.createElement("a");
709
- anchor.setAttribute("href", url);
710
- /* v8 ignore next -- since href is already validated it's probably always present @preserve */
711
- return DOMPurify.sanitize(anchor, {
712
- RETURN_DOM: true,
713
- ...domPurifyConfig
714
- }).querySelector("a").getAttribute("href") ?? "";
715
- };
716
- /**
717
- * Sanitize potentially dangerous URLs, like "javascript:", in image src attributes.
718
- * Returns empty string if the URL is unsafe.
719
- */
720
- var sanitizeImageSrc = (url) => {
721
- const img = document.createElement("img");
722
- img.setAttribute("src", url);
723
- return DOMPurify.sanitize(img, {
724
- RETURN_DOM: true,
725
- ...domPurifyConfig
726
- }).querySelector("img").getAttribute("src") ?? "";
542
+ //#region src/lib/rich-text-editor/rte/instance.ts
543
+ var parseDocument = (schema, doc) => {
544
+ const node = schema.topNodeType.createAndFill(null, doc ? Fragment.fromJSON(schema, doc.content) : null);
545
+ if (!node) throw new Error("Document could not be parsed");
546
+ node.check();
547
+ return node;
727
548
  };
728
- /**
729
- * Escapes a CSS value for insertion into style attribute.
730
- * E.g. "15px; background: red" => "15px"
731
- */
732
- var escapeCssProperty = (value) => value.replace(/[;!].*/, "");
733
- //#endregion
734
- //#region src/lib/rich-text-editor/rte/html-parser.ts
735
- var copy = (obj) => ({ ...obj });
736
- var parseRulesFromSchema = (schema) => ({
737
- nodes: Object.fromEntries(Object.keys(schema.nodes).map((name) => [name, (schema.nodes[name].spec.parseDOM ?? []).map(copy)])),
738
- marks: Object.fromEntries(Object.keys(schema.marks).map((name) => [name, (schema.marks[name].spec.parseDOM ?? []).map(copy)]))
739
- });
740
- var RteHtmlParser = class {
549
+ var RteInstance = class {
741
550
  constructor(config, options) {
742
- this[impl] = new RteHtmlParserImpl(config[impl], options);
551
+ this.options = options;
552
+ this.feature = (Feature, featureId) => {
553
+ return this[impl].getPublicInterface(Feature, featureId);
554
+ };
555
+ this[impl] = new RteInstanceImpl(this, config, options);
743
556
  }
744
557
  /**
745
- * Converts an HTML string to an RteDocument.
558
+ * Returns the current document state.
746
559
  */
747
- parseDocument(html, options) {
748
- return this[impl].parser.parse(this.parseHtml(html, options), { preserveWhitespace: true }).toJSON();
560
+ getDocument() {
561
+ return this[impl].state.doc.toJSON();
749
562
  }
750
563
  /**
751
- * Converts an HTML string to an RteFragment.
752
- */
753
- parseFragment(html, options) {
754
- return this[impl].parser.parseSlice(this.parseHtml(html, options), { preserveWhitespace: true }).content.toJSON() ?? [];
755
- }
756
- parseHtml(html, options) {
757
- const dom = DOMPurify.sanitize(html, {
758
- RETURN_DOM: true,
759
- ...domPurifyConfig
760
- });
761
- const container = document.createDocumentFragment();
762
- container.appendChild(dom);
763
- options?.modifyDom?.(container);
764
- return container;
765
- }
766
- };
767
- var RteHtmlParserImpl = class {
768
- constructor(config, options) {
769
- const rules = parseRulesFromSchema(config.schema);
770
- options?.modifyParseRules?.(rules);
771
- this.parser = buildDomParser(config.schema, rules);
772
- }
773
- transform(html) {
774
- return DOMPurify.sanitize(html);
775
- }
776
- };
777
- var buildDomParser = (schema, { marks, nodes }) => {
778
- const parserRules = [];
779
- const priority = (rule) => rule.priority ?? 50;
780
- function insert(rule) {
781
- let i = 0;
782
- for (; i < parserRules.length; i++) {
783
- const next = parserRules[i];
784
- if (priority(next) < priority(rule)) break;
785
- }
786
- parserRules.splice(i, 0, rule);
787
- }
788
- for (const name in marks) marks[name]?.forEach((rule) => {
789
- insert(rule = copy(rule));
790
- if (!(rule.mark || rule.ignore || rule.clearMark)) rule.mark = name;
791
- });
792
- for (const name in nodes) nodes[name]?.forEach((rule) => {
793
- insert(rule = copy(rule));
794
- if (!(rule.node || rule.ignore || rule.mark)) rule.node = name;
795
- });
796
- parserRules.push({
797
- tag: "br",
798
- closeParent: true
799
- });
800
- return new DOMParser(schema, parserRules);
801
- };
802
- //#endregion
803
- //#region src/lib/rich-text-editor/rte/html-serializer.ts
804
- var RteHtmlSerializer = class {
805
- constructor(config, options) {
806
- this[impl] = new RteHtmlSerializerImpl(config[impl], options);
807
- }
808
- /**
809
- * Converts an RteDocument to an HTML string.
810
- */
811
- serializeDocument(doc, options) {
812
- return this[impl].serializeFragment(doc.content, options);
813
- }
814
- /**
815
- * Converts an RteFragment to an HTML string.
816
- */
817
- serializeFragment(fragment, options) {
818
- return this[impl].serializeFragment(fragment, options);
819
- }
820
- };
821
- var RteHtmlSerializerImpl = class RteHtmlSerializerImpl {
822
- constructor(config, options) {
823
- this.config = config;
824
- const serializers = RteHtmlSerializerImpl.domSerializersFromSchema(config.schema);
825
- Object.assign(serializers.nodes, options?.serializers?.nodes ?? {});
826
- Object.assign(serializers.marks, options?.serializers?.marks ?? {});
827
- this.serializer = new DOMSerializer(serializers.nodes, serializers.marks);
828
- }
829
- static domSerializersFromSchema(schema) {
830
- const result = {
831
- nodes: {},
832
- marks: {}
833
- };
834
- for (const name in schema.marks) {
835
- const toDOM = schema.marks[name].spec.toDOM;
836
- if (toDOM) result.marks[name] = toDOM;
837
- }
838
- for (const name in schema.nodes) {
839
- const toDOM = schema.nodes[name].spec.serializeToDOM ?? schema.nodes[name].spec.toDOM;
840
- if (toDOM) result.nodes[name] = toDOM;
841
- }
842
- result.nodes.text = (node) => document.createTextNode(node.text);
843
- return result;
844
- }
845
- serializeFragment(fragment, options) {
846
- const parsedFragment = Fragment.fromJSON(this.config.schema, fragment);
847
- const serializedFragment = this.serializer.serializeFragment(parsedFragment);
848
- const container = document.createDocumentFragment();
849
- container.appendChild(serializedFragment);
850
- options?.modifyDom?.(container);
851
- const output = document.createElement("div");
852
- output.appendChild(container);
853
- return output.innerHTML;
854
- }
855
- };
856
- //#endregion
857
- //#region src/lib/rich-text-editor/rte/instance.ts
858
- var parseDocument = (schema, doc) => {
859
- const node = schema.topNodeType.createAndFill(null, doc ? Fragment.fromJSON(schema, doc.content) : null);
860
- if (!node) throw new Error("Document could not be parsed");
861
- node.check();
862
- return node;
863
- };
864
- var RteInstance = class {
865
- constructor(config, options) {
866
- this.options = options;
867
- this.feature = (Feature, featureId) => {
868
- return this[impl].getPublicInterface(Feature, featureId);
869
- };
870
- this[impl] = new RteInstanceImpl(this, config, options);
871
- }
872
- /**
873
- * Returns the current document state.
874
- */
875
- getDocument() {
876
- return this[impl].state.doc.toJSON();
877
- }
878
- /**
879
- * Reset the editor to its initial state. Optionally, an initial document can be provided.
564
+ * Reset the editor to its initial state. Optionally, an initial document can be provided.
880
565
  */
881
566
  reset(initialDocument) {
882
567
  this[impl].reset(initialDocument);
@@ -1162,6 +847,322 @@ var RteConfigImpl = class {
1162
847
  }
1163
848
  };
1164
849
  //#endregion
850
+ //#region src/lib/rich-text-editor/rte/utils/ui.ts
851
+ var isPropBinding = (prop) => typeof prop === "function";
852
+ var on = (event, prop, handler) => [
853
+ event,
854
+ prop,
855
+ handler
856
+ ];
857
+ var UiCtx = class {
858
+ constructor(view, rte, props) {
859
+ this.view = view;
860
+ this.rte = rte;
861
+ this.props = props;
862
+ this.bindings = [];
863
+ }
864
+ evalProp(prop) {
865
+ return isPropBinding(prop) ? prop(this) : prop;
866
+ }
867
+ bindProp(prop, bindFn) {
868
+ if (prop === void 0) return;
869
+ else if (isPropBinding(prop)) {
870
+ const binding = () => bindFn(prop(this));
871
+ this.bindings.push(binding);
872
+ binding();
873
+ } else bindFn(prop);
874
+ }
875
+ updateBindings() {
876
+ for (const binding of this.bindings) binding();
877
+ }
878
+ shouldReturnFocusToEditor() {
879
+ return this.evalProp(this.props.shouldReturnFocusToEditor);
880
+ }
881
+ bindToEl(target, props = {}, events = [], children = []) {
882
+ for (const name in props) this.bindProp(props[name], (value) => {
883
+ target[name] = value;
884
+ });
885
+ for (const [name, value, bindFn] of events) if (value) target.addEventListener(name, bindFn(value));
886
+ for (const child of children) target.appendChild(child);
887
+ }
888
+ };
889
+ var createDiv = (ctx, props) => {
890
+ const div = document.createElement("div");
891
+ ctx.bindToEl(div, {
892
+ className: props.className,
893
+ slot: props.slot
894
+ }, [], props.children);
895
+ return div;
896
+ };
897
+ var createAnchor = (ctx, props) => {
898
+ const anchor = document.createElement("a");
899
+ ctx.bindToEl(anchor, {
900
+ href: props.href,
901
+ target: props.target,
902
+ rel: props.rel,
903
+ className: props.className
904
+ }, [], props.children);
905
+ return anchor;
906
+ };
907
+ var wrapperTargets = /* @__PURE__ */ new WeakMap();
908
+ var createOptionalTooltip = (ctx, props) => {
909
+ const tooltip = ctx.rte.createComponent(Tooltip);
910
+ tooltip.setAttribute("exportparts", "vvd-theme-alternate");
911
+ ctx.bindToEl(tooltip, {
912
+ className: "ui-tooltip",
913
+ text: props.label,
914
+ placement: ctx.props.popupPlacement
915
+ });
916
+ ctx.bindProp(props.enabled, (enabled) => {
917
+ if (!enabled) tooltip.open = false;
918
+ tooltip.anchor = enabled ? props.anchor : void 0;
919
+ });
920
+ const wrapper = createDiv(ctx, {
921
+ className: "ui-tooltip-wrapper",
922
+ slot: props.slot,
923
+ children: [props.anchor, tooltip]
924
+ });
925
+ wrapperTargets.set(wrapper, props.anchor);
926
+ return wrapper;
927
+ };
928
+ var createButton = (ctx, props) => {
929
+ const variant = () => ctx.evalProp(props.variant) ?? "toolbar";
930
+ const size = () => variant() === "toolbar" ? "super-condensed" : "condensed";
931
+ const appearanceInactive = () => variant() === "popover-primary" ? "outlined" : "ghost-light";
932
+ const appearanceActive = () => variant() === "toolbar-menu" || variant() === "popover" ? "filled" : appearanceInactive();
933
+ const appearance = () => ctx.evalProp(props.active) ? appearanceActive() : appearanceInactive();
934
+ const connotation = () => ctx.evalProp(props.connotation) ?? (variant() === "toolbar-menu" && ctx.evalProp(props.active) ? "cta" : void 0);
935
+ const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled) || ctx.evalProp(props.disabled));
936
+ const button = ctx.rte.createComponent(Button);
937
+ ctx.bindToEl(button, {
938
+ size,
939
+ appearance,
940
+ disabled,
941
+ icon: props.icon,
942
+ autofocus: props.autofocus,
943
+ connotation,
944
+ [props.icon ? "ariaLabel" : "label"]: props.label,
945
+ pressed: () => Boolean(ctx.evalProp(props.active)),
946
+ ariaPressed: props.active !== void 0 ? () => ctx.evalProp(props.active) ? "true" : "false" : void 0
947
+ }, [on("click", props.onClick, (onClick) => () => {
948
+ if (!onClick() && ctx.shouldReturnFocusToEditor()) ctx.view.focus();
949
+ })]);
950
+ return createOptionalTooltip(ctx, {
951
+ enabled: () => Boolean(ctx.evalProp(props.icon) && !ctx.evalProp(props.noTooltip) && !disabled()),
952
+ label: props.label,
953
+ anchor: button,
954
+ slot: props.slot
955
+ });
956
+ };
957
+ var createMenu = (ctx, props) => {
958
+ const menu = ctx.rte.createComponent(Menu);
959
+ ctx.bindToEl(menu, {
960
+ className: "ui-menu",
961
+ autoDismiss: true,
962
+ ariaLabel: props.label,
963
+ anchor: wrapperTargets.get(props.trigger) ?? props.trigger,
964
+ placement: ctx.props.popupPlacement,
965
+ offset: () => ctx.evalProp(ctx.props.menuOffset) ?? null
966
+ }, [], props.children);
967
+ const fragment = document.createDocumentFragment();
968
+ fragment.appendChild(props.trigger);
969
+ fragment.appendChild(menu);
970
+ return fragment;
971
+ };
972
+ var createMenuItem = (ctx, props) => {
973
+ const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled) || ctx.evalProp(props.disabled));
974
+ const item = ctx.rte.createComponent(MenuItem);
975
+ ctx.bindToEl(item, {
976
+ text: props.text,
977
+ checked: props.checked,
978
+ disabled,
979
+ controlType: "radio",
980
+ checkedAppearance: "tick-only"
981
+ }, [on("change", props.onSelect, (onSelect) => () => {
982
+ if (item.checked && !item.disabled) {
983
+ if (ctx.evalProp(props.checked) !== item.checked) {
984
+ onSelect();
985
+ if (ctx.shouldReturnFocusToEditor()) ctx.view.focus();
986
+ }
987
+ }
988
+ })]);
989
+ return item;
990
+ };
991
+ var createButtonGroup = (ctx, props) => createDiv(ctx, {
992
+ slot: props.slot,
993
+ className: "ui-button-group",
994
+ children: props.children
995
+ });
996
+ function markActive(state, type) {
997
+ const { from, $from, to, empty } = state.selection;
998
+ if (empty) return !!type.isInSet(state.storedMarks || $from.marks());
999
+ else return state.doc.rangeHasMark(from, to, type);
1000
+ }
1001
+ var createMarkToggle = (ctx, props) => createButton(ctx, {
1002
+ label: props.label,
1003
+ icon: props.icon,
1004
+ active: () => markActive(ctx.view.state, props.markType),
1005
+ disabled: () => !toggleMark(props.markType)(ctx.view.state),
1006
+ onClick: () => {
1007
+ toggleMark(props.markType)(ctx.view.state, ctx.view.dispatch);
1008
+ }
1009
+ });
1010
+ var createOption = (ctx, props) => {
1011
+ const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled) || ctx.evalProp(props.disabled));
1012
+ const option = ctx.rte.createComponent(ListboxOption);
1013
+ ctx.bindToEl(option, {
1014
+ value: props.value,
1015
+ text: props.text,
1016
+ disabled
1017
+ });
1018
+ return option;
1019
+ };
1020
+ var createSelect = (ctx, props) => {
1021
+ const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled));
1022
+ const select = ctx.rte.createComponent(Select);
1023
+ select.setAttribute("data-class", "ui-select");
1024
+ ctx.bindToEl(select, {
1025
+ placeholder: " ",
1026
+ ariaLabel: props.label,
1027
+ appearance: "ghost",
1028
+ disabled
1029
+ }, [on("change", props.onSelect, (onSelect) => () => {
1030
+ const value = select.value;
1031
+ if (value) {
1032
+ onSelect(value);
1033
+ if (ctx.shouldReturnFocusToEditor()) ctx.view.focus();
1034
+ }
1035
+ })], props.children);
1036
+ queueMicrotask(() => {
1037
+ ctx.bindProp(props.value, (value) => select.value = value);
1038
+ });
1039
+ let hideTooltip = false;
1040
+ select.addEventListener("vwc-popup:open", () => {
1041
+ hideTooltip = true;
1042
+ ctx.updateBindings();
1043
+ });
1044
+ select.addEventListener("vwc-popup:close", () => {
1045
+ hideTooltip = false;
1046
+ ctx.updateBindings();
1047
+ });
1048
+ return createOptionalTooltip(ctx, {
1049
+ enabled: () => !hideTooltip && !disabled(),
1050
+ label: props.label,
1051
+ anchor: select
1052
+ });
1053
+ };
1054
+ var createDivider = (ctx) => {
1055
+ const divider = ctx.rte.createComponent(Divider);
1056
+ ctx.bindToEl(divider, {
1057
+ className: "ui-divider",
1058
+ orientation: "vertical"
1059
+ });
1060
+ return divider;
1061
+ };
1062
+ var createTextField = (ctx, props) => {
1063
+ const textField = ctx.rte.createComponent(TextField);
1064
+ ctx.bindToEl(textField, {
1065
+ label: props.label,
1066
+ value: props.value,
1067
+ placeholder: props.placeholder,
1068
+ slot: props.slot,
1069
+ autofocus: props.autofocus,
1070
+ type: props.type,
1071
+ helperText: props.helperText
1072
+ }, [on("input", props.onInput, (onInput) => () => {
1073
+ onInput(textField.value);
1074
+ })]);
1075
+ return textField;
1076
+ };
1077
+ var createText = (ctx, props) => {
1078
+ const textNode = document.createTextNode("");
1079
+ ctx.bindToEl(textNode, { textContent: props.text });
1080
+ return textNode;
1081
+ };
1082
+ var createSingleSlot = (ctx, props) => {
1083
+ const slot = document.createElement("slot");
1084
+ slot.name = props.name;
1085
+ let currentEl = null;
1086
+ const listeners = [];
1087
+ function cleanup() {
1088
+ for (const { el, type, handler } of listeners) el.removeEventListener(type, handler);
1089
+ listeners.length = 0;
1090
+ currentEl = null;
1091
+ }
1092
+ for (const [key, prop] of Object.entries(props.assignedProps)) ctx.bindProp(prop, (value) => {
1093
+ if (currentEl) currentEl[key] = value;
1094
+ });
1095
+ function applyPropsAndEvents(el) {
1096
+ for (const [key, prop] of Object.entries(props.assignedProps)) el[key] = ctx.evalProp(prop);
1097
+ for (const [type, handler] of Object.entries(props.assignedEvents)) {
1098
+ const listener = (e) => {
1099
+ handler(e);
1100
+ if (ctx.shouldReturnFocusToEditor()) ctx.view.focus();
1101
+ };
1102
+ el.addEventListener(type, listener);
1103
+ listeners.push({
1104
+ el,
1105
+ type,
1106
+ handler: listener
1107
+ });
1108
+ }
1109
+ }
1110
+ const processSlot = () => {
1111
+ const first = slot.assignedElements({ flatten: true })[0];
1112
+ if (first === currentEl) return;
1113
+ cleanup();
1114
+ if (first) {
1115
+ currentEl = first;
1116
+ applyPropsAndEvents(first);
1117
+ }
1118
+ };
1119
+ slot.addEventListener("slotchange", processSlot);
1120
+ queueMicrotask(processSlot);
1121
+ return slot;
1122
+ };
1123
+ //#endregion
1124
+ //#region src/lib/rich-text-editor/rte/features/internal/history.ts
1125
+ var RteHistoryFeatureImpl = class extends RteFeatureImpl {
1126
+ constructor(config) {
1127
+ super();
1128
+ this.config = config;
1129
+ this.name = "RteHistoryFeature";
1130
+ }
1131
+ getPlugins() {
1132
+ return [this.contribution(history()), this.contribution(keymap({
1133
+ "Mod-z": undo,
1134
+ "Ctrl-y": redo,
1135
+ "Cmd-Z": redo
1136
+ }))];
1137
+ }
1138
+ getToolbarItems() {
1139
+ if (!this.config.showToolbarButtons) return [];
1140
+ return [this.contribution({
1141
+ section: "history",
1142
+ render: (ctx) => createButton(ctx, {
1143
+ label: (ctx) => ctx.rte.getLocale().richTextEditor.undo,
1144
+ icon: "undo-line",
1145
+ disabled: (ctx) => !undo(ctx.view.state),
1146
+ onClick: () => {
1147
+ const { state, dispatch } = ctx.view;
1148
+ undo(state, dispatch);
1149
+ }
1150
+ })
1151
+ }, 1), this.contribution({
1152
+ section: "history",
1153
+ render: (ctx) => createButton(ctx, {
1154
+ label: (ctx) => ctx.rte.getLocale().richTextEditor.redo,
1155
+ icon: "redo-line",
1156
+ disabled: (ctx) => !redo(ctx.view.state),
1157
+ onClick: () => {
1158
+ const { state, dispatch } = ctx.view;
1159
+ redo(state, dispatch);
1160
+ }
1161
+ })
1162
+ }, 2)];
1163
+ }
1164
+ };
1165
+ //#endregion
1165
1166
  //#region src/lib/rich-text-editor/rte/features/internal/basic-text-blocks.style.scss?inline
1166
1167
  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}";
1167
1168
  //#endregion
@@ -1344,6 +1345,7 @@ var RteBaseImpl = class extends RteFeatureImpl {
1344
1345
  return [
1345
1346
  this,
1346
1347
  new RteCoreImpl(),
1348
+ new RteHistoryFeatureImpl({ showToolbarButtons: this.config?.historyToolbarButtons ?? true }),
1347
1349
  new RteBasicTextBlocksImpl({
1348
1350
  heading1: this.config?.heading1 ?? false,
1349
1351
  heading2: this.config?.heading2 ?? false,
@@ -3223,7 +3225,6 @@ var RteInputRuleFeature = featureFacade(RteInputRuleFeatureImpl);
3223
3225
  //#endregion
3224
3226
  //#region src/lib/rich-text-editor/rte/features/keyboard-shortcuts.ts
3225
3227
  function toCommand(handler, rteInstance) {
3226
- if (handler.length === 0) return (_state, _dispatch) => handler();
3227
3228
  return (_state, _dispatch) => handler(rteInstance);
3228
3229
  }
3229
3230
  var RteKeyboardShortcutsFeatureImpl = class extends RteFeatureImpl {