@vonage/vivid 5.20.1 → 5.22.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 (302) hide show
  1. package/accordion/index.cjs +1 -1
  2. package/accordion/index.js +2 -2
  3. package/alert/index.cjs +1 -1
  4. package/alert/index.js +2 -2
  5. package/audio-player/index.cjs +3 -5
  6. package/audio-player/index.js +30 -32
  7. package/avatar/index.cjs +1 -1
  8. package/avatar/index.js +1 -1
  9. package/banner/index.cjs +1 -1
  10. package/banner/index.js +2 -2
  11. package/bundled/affix.cjs +4 -4
  12. package/bundled/affix.js +4 -4
  13. package/bundled/base-color-picker.cjs +2 -2
  14. package/bundled/base-color-picker.js +2 -2
  15. package/bundled/button.cjs +1 -1
  16. package/bundled/button.js +1 -1
  17. package/bundled/definition10.cjs +1 -1
  18. package/bundled/definition10.js +1 -1
  19. package/bundled/definition13.cjs +1 -1
  20. package/bundled/definition13.js +1 -1
  21. package/bundled/definition15.cjs +1 -1
  22. package/bundled/definition15.js +2 -2
  23. package/bundled/definition16.cjs +2 -2
  24. package/bundled/definition16.js +2 -2
  25. package/bundled/definition17.cjs +1 -1
  26. package/bundled/definition17.js +1 -1
  27. package/bundled/definition18.cjs +2 -2
  28. package/bundled/definition18.js +15 -6
  29. package/bundled/definition19.cjs +1 -1
  30. package/bundled/definition19.js +1 -1
  31. package/bundled/definition20.cjs +1 -1
  32. package/bundled/definition20.js +1 -1
  33. package/bundled/definition21.cjs +1 -1
  34. package/bundled/definition21.js +2 -2
  35. package/bundled/definition24.cjs +1 -1
  36. package/bundled/definition24.js +1 -1
  37. package/bundled/definition3.cjs +1 -1
  38. package/bundled/definition3.js +3 -3
  39. package/bundled/definition4.cjs +1 -1
  40. package/bundled/definition4.js +1 -1
  41. package/bundled/definition5.cjs +1 -1
  42. package/bundled/definition5.js +1 -1
  43. package/bundled/definition6.cjs +1 -1
  44. package/bundled/definition6.js +2 -2
  45. package/bundled/definition8.cjs +1 -1
  46. package/bundled/definition8.js +12 -6
  47. package/bundled/definition9.cjs +1 -1
  48. package/bundled/definition9.js +1 -1
  49. package/bundled/form-associated.cjs +1 -1
  50. package/bundled/form-associated.js +2 -2
  51. package/bundled/listbox.cjs +1 -1
  52. package/bundled/listbox.js +5 -5
  53. package/bundled/numberConverter.cjs +1 -1
  54. package/bundled/numberConverter.js +1 -1
  55. package/bundled/slider.template.cjs +1 -1
  56. package/bundled/slider.template.js +1 -1
  57. package/bundled/vivid-element.cjs +1 -1
  58. package/bundled/vivid-element.js +1 -1
  59. package/bundled/with-contextual-help.cjs +1 -1
  60. package/bundled/with-contextual-help.js +1 -1
  61. package/calendar/index.cjs +1 -1
  62. package/calendar/index.js +3 -3
  63. package/color-picker/index.cjs +20 -20
  64. package/color-picker/index.js +22 -22
  65. package/combobox/index.cjs +1 -1
  66. package/combobox/index.js +7 -7
  67. package/country-group/index.cjs +1 -1
  68. package/country-group/index.js +1 -1
  69. package/custom-elements.json +227 -185
  70. package/data-grid/index.cjs +2 -2
  71. package/data-grid/index.js +12 -10
  72. package/dial-pad/index.cjs +1 -1
  73. package/dial-pad/index.js +2 -2
  74. package/dialog/index.cjs +1 -1
  75. package/dialog/index.js +1 -1
  76. package/file-picker/index.cjs +1 -1
  77. package/file-picker/index.js +1 -1
  78. package/header/index.cjs +1 -1
  79. package/header/index.js +1 -1
  80. package/lib/audio-player/audio-player.d.ts +0 -1
  81. package/lib/calendar/helpers/calendar.keyboard-interactions.d.ts +1 -1
  82. package/lib/checkbox/checkbox.d.ts +16 -16
  83. package/lib/color-picker/color-picker.d.ts +16 -16
  84. package/lib/combobox/combobox.d.ts +21 -21
  85. package/lib/date-picker/date-picker.d.ts +26 -26
  86. package/lib/date-range-picker/date-range-picker.d.ts +14 -14
  87. package/lib/date-time-picker/date-time-picker.d.ts +24 -24
  88. package/lib/dial-pad/dial-pad.d.ts +1 -1
  89. package/lib/file-picker/file-picker.d.ts +21 -21
  90. package/lib/menu/menu.d.ts +1 -1
  91. package/lib/number-field/number-field.d.ts +22 -22
  92. package/lib/popup/popup.d.ts +2 -1
  93. package/lib/radio/radio.d.ts +16 -16
  94. package/lib/range-slider/range-slider.d.ts +10 -10
  95. package/lib/rich-text-editor/popover.d.ts +1 -1
  96. package/lib/rich-text-editor/rte/exports.d.ts +1 -0
  97. package/lib/rich-text-editor/rte/feature.d.ts +5 -5
  98. package/lib/rich-text-editor/rte/features/base.d.ts +1 -0
  99. package/lib/rich-text-editor/rte/features/drop-handler.d.ts +1 -2
  100. package/lib/rich-text-editor/rte/features/internal/core.d.ts +8 -0
  101. package/lib/rich-text-editor/rte/features/internal/history.d.ts +6 -0
  102. package/lib/rich-text-editor/rte/features/keyboard-shortcuts.d.ts +1 -1
  103. package/lib/rich-text-editor/rte/features/text-color-picker.d.ts +1 -1
  104. package/lib/rich-text-editor/rte/instance.d.ts +1 -0
  105. package/lib/rich-text-editor/rte/public-interface.d.ts +3 -0
  106. package/lib/rich-text-editor/rte/utils/ui.d.ts +1 -1
  107. package/lib/searchable-select/searchable-select.d.ts +21 -21
  108. package/lib/select/select.d.ts +22 -22
  109. package/lib/simple-color-picker/simple-color-picker.d.ts +5 -5
  110. package/lib/slider/slider.d.ts +5 -5
  111. package/lib/switch/switch.d.ts +5 -5
  112. package/lib/table/table-body.template.d.ts +1 -2
  113. package/lib/table/table-cell.template.d.ts +1 -2
  114. package/lib/table/table-head.template.d.ts +1 -2
  115. package/lib/table/table-row.template.d.ts +1 -2
  116. package/lib/table/table.template.d.ts +1 -2
  117. package/lib/text-area/text-area.d.ts +21 -21
  118. package/lib/text-field/text-field.d.ts +22 -22
  119. package/lib/time-picker/time-picker.d.ts +12 -12
  120. package/locales/de-DE.cjs +178 -4
  121. package/locales/de-DE.js +179 -2
  122. package/locales/en-GB.cjs +9 -4
  123. package/locales/en-GB.js +10 -2
  124. package/locales/en-US.cjs +2 -270
  125. package/locales/en-US.js +1 -267
  126. package/locales/ja-JP.cjs +171 -4
  127. package/locales/ja-JP.js +172 -2
  128. package/locales/zh-CN.cjs +172 -4
  129. package/locales/zh-CN.js +173 -2
  130. package/nav-disclosure/index.cjs +1 -1
  131. package/nav-disclosure/index.js +1 -1
  132. package/nav-item/index.cjs +1 -1
  133. package/nav-item/index.js +1 -1
  134. package/package.json +7 -4
  135. package/pagination/index.cjs +1 -1
  136. package/pagination/index.js +9 -2
  137. package/popover/index.cjs +1 -1
  138. package/popover/index.js +1 -1
  139. package/progress/index.cjs +1 -1
  140. package/progress/index.js +1 -1
  141. package/radio-group/index.cjs +1 -1
  142. package/radio-group/index.js +21 -19
  143. package/range-slider/index.cjs +1 -1
  144. package/range-slider/index.js +1 -1
  145. package/rich-text-editor/index.cjs +13 -12
  146. package/rich-text-editor/index.js +1085 -1057
  147. package/searchable-select/index.cjs +1 -1
  148. package/searchable-select/index.js +1 -1
  149. package/selectable-box/index.cjs +1 -1
  150. package/selectable-box/index.js +6 -1
  151. package/shared/color-picker/base-color-picker.d.ts +5 -5
  152. package/shared/feedback/mixins.d.ts +1 -1
  153. package/shared/foundation/button/button.d.ts +5 -5
  154. package/shared/foundation/form-associated/form-associated.d.ts +10 -10
  155. package/shared/foundation/listbox/listbox.d.ts +1 -1
  156. package/shared/patterns/form-elements/form-element.d.ts +5 -5
  157. package/shared/patterns/form-elements/with-contextual-help.d.ts +5 -5
  158. package/shared/patterns/form-elements/with-error-text.d.ts +6 -6
  159. package/shared/picker-field/mixins/calendar-picker.d.ts +6 -6
  160. package/shared/picker-field/mixins/calendar-picker.template.d.ts +6 -6
  161. package/shared/picker-field/mixins/min-max-calendar-picker.d.ts +8 -8
  162. package/shared/picker-field/mixins/single-date-picker.d.ts +6 -6
  163. package/shared/picker-field/mixins/single-value-picker.d.ts +6 -6
  164. package/shared/picker-field/mixins/time-selection-picker.d.ts +6 -6
  165. package/shared/picker-field/mixins/time-selection-picker.template.d.ts +12 -12
  166. package/shared/picker-field/picker-field.d.ts +21 -21
  167. package/shared/utils/slottable-request.d.ts +1 -1
  168. package/switch/index.cjs +2 -2
  169. package/switch/index.js +2 -2
  170. package/table/index.cjs +5 -5
  171. package/table/index.js +5 -5
  172. package/tabs/index.cjs +1 -1
  173. package/tabs/index.js +3 -3
  174. package/tag/index.cjs +1 -1
  175. package/tag/index.js +1 -1
  176. package/tree-view/index.cjs +1 -1
  177. package/tree-view/index.js +0 -2
  178. package/unbundled/affix.cjs +4 -4
  179. package/unbundled/affix.js +4 -4
  180. package/unbundled/base-color-picker.cjs +3 -3
  181. package/unbundled/base-color-picker.js +3 -3
  182. package/unbundled/button.cjs +5 -5
  183. package/unbundled/button.js +5 -5
  184. package/unbundled/chunk.cjs +15 -0
  185. package/unbundled/chunk.js +13 -0
  186. package/unbundled/definition10.cjs +15 -4
  187. package/unbundled/definition10.js +15 -4
  188. package/unbundled/definition11.cjs +3 -3
  189. package/unbundled/definition11.js +3 -3
  190. package/unbundled/definition12.cjs +1 -1
  191. package/unbundled/definition12.js +1 -1
  192. package/unbundled/definition13.cjs +2 -2
  193. package/unbundled/definition13.js +2 -2
  194. package/unbundled/definition14.cjs +73 -76
  195. package/unbundled/definition14.js +73 -76
  196. package/unbundled/definition15.cjs +1 -1
  197. package/unbundled/definition15.js +1 -1
  198. package/unbundled/definition17.cjs +2 -2
  199. package/unbundled/definition17.js +2 -2
  200. package/unbundled/definition19.cjs +2 -1
  201. package/unbundled/definition19.js +2 -1
  202. package/unbundled/definition2.cjs +1 -1
  203. package/unbundled/definition2.js +1 -1
  204. package/unbundled/definition21.cjs +3 -3
  205. package/unbundled/definition21.js +3 -3
  206. package/unbundled/definition23.cjs +2 -2
  207. package/unbundled/definition23.js +2 -2
  208. package/unbundled/definition24.cjs +1 -1
  209. package/unbundled/definition24.js +1 -1
  210. package/unbundled/definition27.cjs +1 -1
  211. package/unbundled/definition27.js +1 -1
  212. package/unbundled/definition28.cjs +23 -23
  213. package/unbundled/definition28.js +23 -23
  214. package/unbundled/definition29.cjs +1 -1
  215. package/unbundled/definition29.js +1 -1
  216. package/unbundled/definition3.cjs +2 -2
  217. package/unbundled/definition3.js +2 -2
  218. package/unbundled/definition30.cjs +8 -8
  219. package/unbundled/definition30.js +8 -8
  220. package/unbundled/definition32.cjs +1 -1
  221. package/unbundled/definition32.js +1 -1
  222. package/unbundled/definition34.cjs +9 -8
  223. package/unbundled/definition34.js +9 -8
  224. package/unbundled/definition35.cjs +1 -1
  225. package/unbundled/definition35.js +1 -1
  226. package/unbundled/definition39.cjs +2 -2
  227. package/unbundled/definition39.js +2 -2
  228. package/unbundled/definition40.cjs +1 -1
  229. package/unbundled/definition40.js +1 -1
  230. package/unbundled/definition43.cjs +1 -1
  231. package/unbundled/definition43.js +1 -1
  232. package/unbundled/definition44.cjs +1 -1
  233. package/unbundled/definition44.js +1 -1
  234. package/unbundled/definition48.cjs +1 -1
  235. package/unbundled/definition48.js +1 -1
  236. package/unbundled/definition49.cjs +1 -1
  237. package/unbundled/definition49.js +1 -1
  238. package/unbundled/definition5.cjs +1 -1
  239. package/unbundled/definition5.js +1 -1
  240. package/unbundled/definition53.cjs +10 -3
  241. package/unbundled/definition53.js +10 -3
  242. package/unbundled/definition55.cjs +1 -1
  243. package/unbundled/definition55.js +1 -1
  244. package/unbundled/definition56.cjs +1 -1
  245. package/unbundled/definition56.js +1 -1
  246. package/unbundled/definition57.cjs +4 -5
  247. package/unbundled/definition57.js +4 -5
  248. package/unbundled/definition58.cjs +20 -19
  249. package/unbundled/definition58.js +20 -19
  250. package/unbundled/definition59.cjs +1 -1
  251. package/unbundled/definition59.js +1 -1
  252. package/unbundled/definition60.cjs +890 -851
  253. package/unbundled/definition60.js +890 -851
  254. package/unbundled/definition61.cjs +13 -7
  255. package/unbundled/definition61.js +13 -7
  256. package/unbundled/definition63.cjs +1 -1
  257. package/unbundled/definition63.js +1 -1
  258. package/unbundled/definition64.cjs +4 -1
  259. package/unbundled/definition64.js +4 -1
  260. package/unbundled/definition65.cjs +2 -1
  261. package/unbundled/definition65.js +2 -1
  262. package/unbundled/definition69.cjs +4 -3
  263. package/unbundled/definition69.js +4 -3
  264. package/unbundled/definition7.cjs +2 -2
  265. package/unbundled/definition7.js +2 -2
  266. package/unbundled/definition71.cjs +1 -1
  267. package/unbundled/definition71.js +1 -1
  268. package/unbundled/definition72.cjs +5 -5
  269. package/unbundled/definition72.js +5 -5
  270. package/unbundled/definition73.cjs +3 -3
  271. package/unbundled/definition73.js +3 -3
  272. package/unbundled/definition75.cjs +1 -1
  273. package/unbundled/definition75.js +1 -1
  274. package/unbundled/definition78.cjs +1 -1
  275. package/unbundled/definition78.js +1 -1
  276. package/unbundled/definition79.cjs +0 -2
  277. package/unbundled/definition79.js +0 -2
  278. package/unbundled/definition80.cjs +1 -1
  279. package/unbundled/definition80.js +1 -1
  280. package/unbundled/definition9.cjs +2 -2
  281. package/unbundled/definition9.js +2 -2
  282. package/unbundled/en-US.cjs +449 -0
  283. package/unbundled/en-US.js +445 -0
  284. package/unbundled/form-associated.cjs +2 -2
  285. package/unbundled/form-associated.js +2 -2
  286. package/unbundled/listbox.cjs +7 -7
  287. package/unbundled/listbox.js +7 -7
  288. package/unbundled/localized.cjs +2 -2
  289. package/unbundled/localized.js +1 -1
  290. package/unbundled/numberConverter.cjs +1 -1
  291. package/unbundled/numberConverter.js +1 -1
  292. package/unbundled/slider.template.cjs +1 -1
  293. package/unbundled/slider.template.js +1 -1
  294. package/unbundled/slottable-request.cjs +5 -5
  295. package/unbundled/slottable-request.js +5 -5
  296. package/unbundled/vivid-element.cjs +1 -1
  297. package/unbundled/vivid-element.js +1 -1
  298. package/unbundled/with-contextual-help.cjs +4 -1
  299. package/unbundled/with-contextual-help.js +4 -1
  300. package/video-player/index.cjs +1 -1
  301. package/video-player/index.js +1 -1
  302. package/vivid.api.json +60 -18
@@ -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");
@@ -113,12 +113,20 @@ var Kind = {
113
113
  default: {
114
114
  placement: void 0,
115
115
  placementStrategy: require_definition$2.PlacementStrategy.AutoPlacementHorizontal,
116
+ shiftCrossAxis: false,
116
117
  strategy: "absolute"
117
118
  },
118
119
  autocomplete: {
119
120
  placement: "bottom-start",
120
121
  placementStrategy: require_definition$2.PlacementStrategy.Flip,
122
+ shiftCrossAxis: false,
121
123
  strategy: "fixed"
124
+ },
125
+ imageOptions: {
126
+ placement: "bottom",
127
+ placementStrategy: require_definition$2.PlacementStrategy.Flip,
128
+ shiftCrossAxis: true,
129
+ strategy: "absolute"
122
130
  }
123
131
  };
124
132
  var popoverTemplate = (context) => {
@@ -127,6 +135,7 @@ var popoverTemplate = (context) => {
127
135
  :anchor="${(x) => x.anchorEl}"
128
136
  :placement="${(x) => Kind[x.kind].placement}"
129
137
  :placementStrategy="${(x) => Kind[x.kind].placementStrategy}"
138
+ :shiftCrossAxis="${(x) => Kind[x.kind].shiftCrossAxis}"
130
139
  :open="${(x) => x.open}"
131
140
  :offset="${(x) => x.offset}"
132
141
  :strategy="${(x) => Kind[x.kind].strategy}"
@@ -240,927 +249,942 @@ var FeatureState = class {
240
249
  //#region src/lib/rich-text-editor/rte/features/internal/core.style.scss?inline
241
250
  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
251
  //#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 = [];
252
+ //#region src/lib/rich-text-editor/rte/features/internal/foreign-html.ts
253
+ var RteForeignHtmlFeatureImpl = class extends require_slottable_request.RteFeatureImpl {
254
+ constructor(..._args) {
255
+ super(..._args);
256
+ this.name = "RteForeignHtmlFeature";
256
257
  }
257
- evalProp(prop) {
258
- return isPropBinding(prop) ? prop(this) : prop;
258
+ getPlugins(rte) {
259
+ return [this.contribution(new prosemirror_state.Plugin({ props: {
260
+ transformPastedHTML: (html) => rte.foreignHtmlParser[require_slottable_request.impl].transform(html),
261
+ clipboardParser: rte.foreignHtmlParser[require_slottable_request.impl].parser,
262
+ clipboardSerializer: rte.foreignHtmlSerializer[require_slottable_request.impl].serializer
263
+ } }))];
259
264
  }
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);
265
+ };
266
+ //#endregion
267
+ //#region src/lib/rich-text-editor/rte/features/internal/cursor-fix.ts
268
+ /**
269
+ * When the cursor is positioned after an inline atom node at the end of a line,
270
+ * browsers may display the cursor inside the atom.
271
+ */
272
+ var atomCursorFix = ($cursor) => {
273
+ if (!($cursor.parentOffset === $cursor.parent.content.size)) return null;
274
+ const nodeBefore = $cursor.nodeBefore;
275
+ if (nodeBefore && nodeBefore.isInline && nodeBefore.isAtom && !nodeBefore.isText) return {};
276
+ return null;
277
+ };
278
+ var RteCursorFixFeatureImpl = class extends require_slottable_request.RteFeatureImpl {
279
+ constructor(..._args) {
280
+ super(..._args);
281
+ this.name = "RteCursorFix";
267
282
  }
268
- updateBindings() {
269
- for (const binding of this.bindings) binding();
283
+ getPlugins(rte) {
284
+ const cursorFixes = [atomCursorFix];
285
+ for (const markType of Object.values(rte.schema.marks)) {
286
+ const spec = markType.spec;
287
+ if (spec.cursorFix) cursorFixes.push(spec.cursorFix);
288
+ }
289
+ return [this.contribution(new prosemirror_state.Plugin({ props: { decorations: (state) => {
290
+ const { $cursor } = state.selection;
291
+ if (!$cursor) return null;
292
+ let cursorFix = null;
293
+ for (const fn of cursorFixes) {
294
+ const result = fn($cursor, state);
295
+ if (result) Object.assign(cursorFix ??= {}, result);
296
+ }
297
+ if (!cursorFix) return null;
298
+ return prosemirror_view.DecorationSet.create(state.doc, [prosemirror_view.Decoration.widget($cursor.pos, () => {
299
+ const span = document.createElement("span");
300
+ span.textContent = "​";
301
+ for (const [prop, value] of Object.entries(cursorFix)) span.style.setProperty(prop, value);
302
+ return span;
303
+ }, { side: -1 })]);
304
+ } } }))];
270
305
  }
271
- shouldReturnFocusToEditor() {
272
- return this.evalProp(this.props.shouldReturnFocusToEditor);
306
+ };
307
+ require_slottable_request.featureFacade(RteCursorFixFeatureImpl);
308
+ //#endregion
309
+ //#region src/lib/rich-text-editor/rte/features/internal/core.ts
310
+ /**
311
+ * Plugin to bring state from the host web component into ProseMirror.
312
+ */
313
+ var hostBridgePlugin = new prosemirror_state.Plugin({ state: {
314
+ init() {
315
+ return null;
316
+ },
317
+ apply(tr, value) {
318
+ const meta = tr.getMeta(hostBridgePlugin);
319
+ if (meta) return meta;
320
+ else return value;
273
321
  }
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);
322
+ } });
323
+ var RteCoreImpl = class extends require_slottable_request.RteFeatureImpl {
324
+ constructor(..._args) {
325
+ super(..._args);
326
+ this.name = "RteCore";
327
+ this.disabled = new FeatureState(false);
328
+ this.scrollThreshold = void 0;
329
+ this.scrollMargin = void 0;
330
+ }
331
+ getStyles() {
332
+ return [
333
+ this.contribution(prosemirror_default),
334
+ this.contribution(core_style_default),
335
+ this.contribution(ui_style_default)
336
+ ];
337
+ }
338
+ getPlugins(rte) {
339
+ const enterKeyChainCommands = (0, prosemirror_commands.chainCommands)(prosemirror_commands.newlineInCode, prosemirror_commands.createParagraphNear, prosemirror_commands.liftEmptyBlock, (0, prosemirror_commands.splitBlockAs)((node, atEnd, $from) => {
340
+ if (!atEnd) return {
341
+ type: node.type,
342
+ attrs: node.attrs
343
+ };
344
+ return {
345
+ type: defaultTextblockForMatch($from.node($from.depth - 1).contentMatchAt($from.indexAfter($from.depth - 1))),
346
+ attrs: rte.textblockAttrs.extractFromNode(node)
347
+ };
348
+ }));
349
+ return [
350
+ this.contribution(this.disabled.plugin),
351
+ this.contribution(new prosemirror_state.Plugin({
352
+ props: {
353
+ editable: () => !this.disabled.getValue(rte),
354
+ handleDOMEvents: { click: (_view, event) => {
355
+ if (this.disabled.getValue(rte)) {
356
+ event.preventDefault();
357
+ return true;
358
+ }
359
+ return false;
360
+ } }
361
+ },
362
+ view: (view) => {
363
+ const popovers = view.dom.getRootNode().querySelector(".popovers");
364
+ const updateDisabled = () => {
365
+ const disabled = this.disabled.getValue(rte);
366
+ popovers.classList.toggle("popovers--disabled", disabled);
367
+ view.dom.parentElement.classList.toggle("editor--disabled", disabled);
368
+ };
369
+ updateDisabled();
370
+ return { update: () => {
371
+ updateDisabled();
372
+ } };
373
+ }
374
+ })),
375
+ this.contribution((0, prosemirror_keymap.keymap)({
376
+ ...prosemirror_commands.baseKeymap,
377
+ Enter: enterKeyChainCommands,
378
+ "Shift-Enter": enterKeyChainCommands
379
+ })),
380
+ this.contribution((0, prosemirror_dropcursor.dropCursor)()),
381
+ this.contribution((0, prosemirror_gapcursor.gapCursor)()),
382
+ this.contribution(hostBridgePlugin)
383
+ ];
384
+ }
385
+ getFeatures() {
386
+ return [
387
+ this,
388
+ new RteForeignHtmlFeatureImpl(),
389
+ new RteCursorFixFeatureImpl()
390
+ ];
280
391
  }
281
392
  };
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) => {
393
+ require_slottable_request.featureFacade(RteCoreImpl);
394
+ //#endregion
395
+ //#region src/lib/rich-text-editor/rte/utils/sanitization.ts
396
+ var DEFAULT_ALLOWED_URI_REGEXP = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
397
+ var ALLOWED_URI_REGEXP_WITH_BLOB = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|blob):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
398
+ var ATTR_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
399
+ var domPurifyConfig = { ALLOWED_URI_REGEXP: ALLOWED_URI_REGEXP_WITH_BLOB };
400
+ /**
401
+ * Sanitize potentially dangerous URLs, like "javascript:", in anchor href attributes.
402
+ * Returns empty string if the URL is unsafe.
403
+ */
404
+ var sanitizeLinkHref = (url) => {
405
+ if (!DEFAULT_ALLOWED_URI_REGEXP.test(url.replace(ATTR_WHITESPACE, ""))) return "";
291
406
  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;
407
+ anchor.setAttribute("href", url);
408
+ /* v8 ignore next -- since href is already validated it's probably always present @preserve */
409
+ return dompurify.default.sanitize(anchor, {
410
+ RETURN_DOM: true,
411
+ ...domPurifyConfig
412
+ }).querySelector("a").getAttribute("href") ?? "";
299
413
  };
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;
414
+ /**
415
+ * Sanitize potentially dangerous URLs, like "javascript:", in image src attributes.
416
+ * Returns empty string if the URL is unsafe.
417
+ */
418
+ var sanitizeImageSrc = (url) => {
419
+ const img = document.createElement("img");
420
+ img.setAttribute("src", url);
421
+ return dompurify.default.sanitize(img, {
422
+ RETURN_DOM: true,
423
+ ...domPurifyConfig
424
+ }).querySelector("img").getAttribute("src") ?? "";
320
425
  };
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
426
+ /**
427
+ * Escapes a CSS value for insertion into style attribute.
428
+ * E.g. "15px; background: red" => "15px"
429
+ */
430
+ var escapeCssProperty = (value) => value.replace(/[;!].*/, "");
431
+ //#endregion
432
+ //#region src/lib/rich-text-editor/rte/html-parser.ts
433
+ var copy = (obj) => ({ ...obj });
434
+ var parseRulesFromSchema = (schema) => ({
435
+ nodes: Object.fromEntries(Object.keys(schema.nodes).map((name) => [name, (schema.nodes[name].spec.parseDOM ?? []).map(copy)])),
436
+ marks: Object.fromEntries(Object.keys(schema.marks).map((name) => [name, (schema.marks[name].spec.parseDOM ?? []).map(copy)]))
388
437
  });
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);
438
+ var RteHtmlParser = class {
439
+ constructor(config, options) {
440
+ this[require_slottable_request.impl] = new RteHtmlParserImpl(config[require_slottable_request.impl], options);
441
+ }
442
+ /**
443
+ * Converts an HTML string to an RteDocument.
444
+ */
445
+ parseDocument(html, options) {
446
+ return this[require_slottable_request.impl].parser.parse(this.parseHtml(html, options), { preserveWhitespace: true }).toJSON();
447
+ }
448
+ /**
449
+ * Converts an HTML string to an RteFragment.
450
+ */
451
+ parseFragment(html, options) {
452
+ return this[require_slottable_request.impl].parser.parseSlice(this.parseHtml(html, options), { preserveWhitespace: true }).content.toJSON() ?? [];
453
+ }
454
+ parseHtml(html, options) {
455
+ const dom = dompurify.default.sanitize(html, {
456
+ RETURN_DOM: true,
457
+ ...domPurifyConfig
458
+ });
459
+ const container = document.createDocumentFragment();
460
+ container.appendChild(dom);
461
+ options?.modifyDom?.(container);
462
+ return container;
401
463
  }
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
464
  };
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();
465
+ var RteHtmlParserImpl = class {
466
+ constructor(config, options) {
467
+ const rules = parseRulesFromSchema(config.schema);
468
+ options?.modifyParseRules?.(rules);
469
+ this.parser = buildDomParser(config.schema, rules);
470
+ }
471
+ transform(html) {
472
+ return dompurify.default.sanitize(html);
473
+ }
474
+ };
475
+ var buildDomParser = (schema, { marks, nodes }) => {
476
+ const parserRules = [];
477
+ const priority = (rule) => rule.priority ?? 50;
478
+ function insert(rule) {
479
+ let i = 0;
480
+ for (; i < parserRules.length; i++) {
481
+ const next = parserRules[i];
482
+ if (priority(next) < priority(rule)) break;
427
483
  }
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();
484
+ parserRules.splice(i, 0, rule);
485
+ }
486
+ for (const name in marks) marks[name]?.forEach((rule) => {
487
+ insert(rule = copy(rule));
488
+ if (!(rule.mark || rule.ignore || rule.clearMark)) rule.mark = name;
440
489
  });
441
- return createOptionalTooltip(ctx, {
442
- enabled: () => !hideTooltip && !disabled(),
443
- label: props.label,
444
- anchor: select
490
+ for (const name in nodes) nodes[name]?.forEach((rule) => {
491
+ insert(rule = copy(rule));
492
+ if (!(rule.node || rule.ignore || rule.mark)) rule.node = name;
445
493
  });
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"
494
+ parserRules.push({
495
+ tag: "br",
496
+ closeParent: true
452
497
  });
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;
498
+ return new prosemirror_model.DOMParser(schema, parserRules);
469
499
  };
470
- var createText = (ctx, props) => {
471
- const textNode = document.createTextNode("");
472
- ctx.bindToEl(textNode, { textContent: props.text });
473
- return textNode;
500
+ //#endregion
501
+ //#region src/lib/rich-text-editor/rte/html-serializer.ts
502
+ var RteHtmlSerializer = class {
503
+ constructor(config, options) {
504
+ this[require_slottable_request.impl] = new RteHtmlSerializerImpl(config[require_slottable_request.impl], options);
505
+ }
506
+ /**
507
+ * Converts an RteDocument to an HTML string.
508
+ */
509
+ serializeDocument(doc, options) {
510
+ return this[require_slottable_request.impl].serializeFragment(doc.content, options);
511
+ }
512
+ /**
513
+ * Converts an RteFragment to an HTML string.
514
+ */
515
+ serializeFragment(fragment, options) {
516
+ return this[require_slottable_request.impl].serializeFragment(fragment, options);
517
+ }
474
518
  };
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;
519
+ var RteHtmlSerializerImpl = class RteHtmlSerializerImpl {
520
+ constructor(config, options) {
521
+ this.config = config;
522
+ const serializers = RteHtmlSerializerImpl.domSerializersFromSchema(config.schema);
523
+ Object.assign(serializers.nodes, options?.serializers?.nodes ?? {});
524
+ Object.assign(serializers.marks, options?.serializers?.marks ?? {});
525
+ this.serializer = new prosemirror_model.DOMSerializer(serializers.nodes, serializers.marks);
484
526
  }
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
- });
527
+ static domSerializersFromSchema(schema) {
528
+ const result = {
529
+ nodes: {},
530
+ marks: {}
531
+ };
532
+ for (const name in schema.marks) {
533
+ const toDOM = schema.marks[name].spec.toDOM;
534
+ if (toDOM) result.marks[name] = toDOM;
501
535
  }
536
+ for (const name in schema.nodes) {
537
+ const toDOM = schema.nodes[name].spec.serializeToDOM ?? schema.nodes[name].spec.toDOM;
538
+ if (toDOM) result.nodes[name] = toDOM;
539
+ }
540
+ result.nodes.text = (node) => document.createTextNode(node.text);
541
+ return result;
542
+ }
543
+ serializeFragment(fragment, options) {
544
+ const parsedFragment = prosemirror_model.Fragment.fromJSON(this.config.schema, fragment);
545
+ const serializedFragment = this.serializer.serializeFragment(parsedFragment);
546
+ const container = document.createDocumentFragment();
547
+ container.appendChild(serializedFragment);
548
+ options?.modifyDom?.(container);
549
+ const output = document.createElement("div");
550
+ output.appendChild(container);
551
+ return output.innerHTML;
502
552
  }
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
553
  };
516
554
  //#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";
555
+ //#region src/lib/rich-text-editor/rte/instance.ts
556
+ var parseDocument = (schema, doc) => {
557
+ const node = schema.topNodeType.createAndFill(null, doc ? prosemirror_model.Fragment.fromJSON(schema, doc.content) : null);
558
+ if (!node) throw new Error("Document could not be parsed");
559
+ node.check();
560
+ return node;
561
+ };
562
+ var RteInstance = class {
563
+ constructor(config, options) {
564
+ this.options = options;
565
+ this.feature = (Feature, featureId) => {
566
+ return this[require_slottable_request.impl].getPublicInterface(Feature, featureId);
567
+ };
568
+ this[require_slottable_request.impl] = new RteInstanceImpl(this, config, options);
522
569
  }
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
- }))];
570
+ /**
571
+ * Returns the current document state.
572
+ */
573
+ getDocument() {
574
+ return this[require_slottable_request.impl].state.doc.toJSON();
529
575
  }
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)];
576
+ /**
577
+ * Reset the editor to its initial state. Optionally, an initial document can be provided.
578
+ */
579
+ reset(initialDocument) {
580
+ this[require_slottable_request.impl].reset(initialDocument);
554
581
  }
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";
582
+ /**
583
+ * Replaces the current selection with the given content.
584
+ * If no text is selected, this inserts the content at the cursor position.
585
+ */
586
+ replaceSelection(content, options) {
587
+ const { state, tr, schema, dispatchTransaction } = this[require_slottable_request.impl];
588
+ tr.replaceSelection(new prosemirror_model.Slice(prosemirror_model.Fragment.fromJSON(schema, content), 0, 0));
589
+ tr.doc.check();
590
+ const $from = tr.doc.resolve(tr.mapping.map(state.selection.from));
591
+ const $to = tr.doc.resolve(tr.selection.to);
592
+ const [$head, $anchor] = options?.cursorPlacement === "start" ? [$from, $to] : [$to, $from];
593
+ if (options?.selectContent) tr.setSelection(state.selection instanceof prosemirror_state.AllSelection ? new prosemirror_state.AllSelection(tr.doc) : prosemirror_state.TextSelection.between($anchor, $head));
594
+ else tr.setSelection(prosemirror_state.TextSelection.between($head, $head));
595
+ dispatchTransaction(tr);
562
596
  }
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
- } }))];
597
+ /**
598
+ * Replaces the entire document with the given new document.
599
+ * Unlike reset, this preserves the rest of the editor state. The undo history is preserved, so the user can undo the replacement.
600
+ */
601
+ replaceDocument(newDocument, options) {
602
+ const { state, tr, schema, dispatchTransaction } = this[require_slottable_request.impl];
603
+ tr.replaceWith(0, state.doc.content.size, parseDocument(schema, newDocument));
604
+ if (options?.selectContent) tr.setSelection(new prosemirror_state.AllSelection(tr.doc));
605
+ else if (options?.cursorPlacement === "end") tr.setSelection(prosemirror_state.TextSelection.atEnd(tr.doc));
606
+ else tr.setSelection(prosemirror_state.TextSelection.atStart(tr.doc));
607
+ dispatchTransaction(tr);
608
+ }
609
+ /**
610
+ * Scrolls the cursor into view.
611
+ * Has no effect if the editor is not currently mounted.
612
+ */
613
+ scrollIntoView() {
614
+ const instance = this[require_slottable_request.impl];
615
+ if (!instance.view) return;
616
+ instance.dispatchTransaction(instance.tr.scrollIntoView());
569
617
  }
570
618
  };
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";
619
+ var RteInstanceImpl = class {
620
+ getFeature(name) {
621
+ const f = this.config.featureMap.get(name);
622
+ if (!f) throw new Error(`Feature not found: ${name}`);
623
+ return f;
587
624
  }
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);
625
+ getPublicInterface(Feature, featureId) {
626
+ const instances = this.config.featureFacadesMap.get(Feature);
627
+ if (!instances || instances.length === 0) throw new Error(`Feature not found`);
628
+ if (instances[0].featureId !== void 0) {
629
+ if (featureId === void 0) throw new Error(`No featureId provided for multi-instance feature.`);
630
+ const instance = instances.find((f) => f.featureId === featureId);
631
+ if (!instance) throw new Error(`Feature with id "${featureId}" not found`);
632
+ return instance.getPublicInterface(this);
593
633
  }
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
- } } }))];
634
+ if (featureId !== void 0) throw new Error(`Feature does not support featureId`);
635
+ return instances[0].getPublicInterface(this);
610
636
  }
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;
637
+ constructor(instanceFacade, configFacade, options) {
638
+ this.options = options;
639
+ this.view = null;
640
+ this.dispatchTransaction = (tr) => {
641
+ const prevState = this.state;
642
+ this.state = this.state.apply(tr);
643
+ this.view?.updateState(this.state);
644
+ if (prevState.doc !== this.state.doc) this.options?.onChange?.();
645
+ return this.state;
646
+ };
647
+ this.facade = instanceFacade;
648
+ const config = configFacade[require_slottable_request.impl];
649
+ this.config = config;
650
+ this.schema = config.schema;
651
+ this.textblockAttrs = config.textblockAttrs;
652
+ this.features = config.features;
653
+ this.foreignHtmlParser = options?.foreignHtmlParser ?? new RteHtmlParser(configFacade);
654
+ this.foreignHtmlSerializer = options?.foreignHtmlSerializer ?? new RteHtmlSerializer(configFacade);
655
+ this.styles = new _microsoft_fast_element.ElementStyles(require_slottable_request.sortedContributions(config.features.flatMap((f) => f.getStyles())));
656
+ this.initState(options?.initialDocument);
626
657
  }
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);
658
+ initState(initialDoc) {
659
+ this.state = prosemirror_state.EditorState.create({
660
+ doc: parseDocument(this.schema, initialDoc),
661
+ schema: this.config.schema,
662
+ plugins: require_slottable_request.sortedContributions(this.config.features.flatMap((feature) => feature.getPlugins(this)))
663
+ });
633
664
  }
634
- getStyles() {
635
- return [
636
- this.contribution(prosemirror_default),
637
- this.contribution(core_style_default),
638
- this.contribution(ui_style_default)
639
- ];
665
+ reset(initialDocument) {
666
+ const currentHostState = hostBridgePlugin.getState(this.state);
667
+ this.initState(initialDocument);
668
+ this.updateHostState(currentHostState);
669
+ this.view?.updateState(this.state);
640
670
  }
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
- ];
687
- }
688
- getFeatures() {
689
- return [
690
- this,
691
- new RteHistoryFeatureImpl(),
692
- new RteForeignHtmlFeatureImpl(),
693
- new RteCursorFixFeatureImpl()
694
- ];
695
- }
696
- };
697
- require_slottable_request.featureFacade(RteCoreImpl);
698
- //#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") ?? "";
729
- };
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 {
743
- constructor(config, options) {
744
- this[require_slottable_request.impl] = new RteHtmlParserImpl(config[require_slottable_request.impl], options);
671
+ createView(target) {
672
+ const core = this.config.featureMap.get("RteCore");
673
+ this.view = new prosemirror_view.EditorView((editor) => {
674
+ editor.part = "editor";
675
+ target.appendChild(editor);
676
+ }, {
677
+ state: this.state,
678
+ dispatchTransaction: this.dispatchTransaction,
679
+ ...core?.scrollThreshold !== void 0 && { scrollThreshold: core.scrollThreshold },
680
+ ...core?.scrollMargin !== void 0 && { scrollMargin: core.scrollMargin }
681
+ });
745
682
  }
746
- /**
747
- * Converts an HTML string to an RteDocument.
748
- */
749
- parseDocument(html, options) {
750
- return this[require_slottable_request.impl].parser.parse(this.parseHtml(html, options), { preserveWhitespace: true }).toJSON();
683
+ destroyViewIfNeeded() {
684
+ if (this.view) {
685
+ this.view.destroy();
686
+ this.view = null;
687
+ }
751
688
  }
752
- /**
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() ?? [];
689
+ createComponent(type) {
690
+ return document.createElement(this.hostState().ctx.tagFor(type, true));
757
691
  }
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;
692
+ getLocale() {
693
+ return this.hostState().locale;
767
694
  }
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);
695
+ get tr() {
696
+ return this.state.tr;
774
697
  }
775
- transform(html) {
776
- return dompurify.default.sanitize(html);
698
+ hostState() {
699
+ const state = hostBridgePlugin.getState(this.state);
700
+ if (!state) throw new Error("No host state available");
701
+ return state;
777
702
  }
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);
703
+ updateHostState(hostState) {
704
+ this.dispatchTransaction(this.tr.setMeta(hostBridgePlugin, hostState));
789
705
  }
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
706
  };
804
707
  //#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);
708
+ //#region src/lib/rich-text-editor/rte/utils/textblock-attrs.ts
709
+ var TextblockAttrs = class {
710
+ constructor(specs) {
711
+ this.specs = specs;
712
+ this.attrs = {};
713
+ for (const spec of specs) this.attrs[spec.name] = { default: spec.default };
809
714
  }
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);
715
+ fromDOM(dom) {
716
+ return Object.assign({}, ...this.specs.map((s) => ({ [s.name]: s.fromDOM(dom) })));
815
717
  }
816
- /**
817
- * Converts an RteFragment to an HTML string.
818
- */
819
- serializeFragment(fragment, options) {
820
- return this[require_slottable_request.impl].serializeFragment(fragment, options);
718
+ getStyle(node) {
719
+ return this.specs.flatMap((s) => s.toStyles(node)).join("; ");
821
720
  }
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);
721
+ getDOMAttrsProperties(node) {
722
+ if (!this.specs.length) return {};
723
+ return { style: this.getStyle(node) };
830
724
  }
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;
725
+ getDOMAttrs(node) {
726
+ if (!this.specs.length) return [];
727
+ return [this.getDOMAttrsProperties(node)];
846
728
  }
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;
729
+ extractFromNode(node) {
730
+ return Object.assign({}, ...Object.keys(this.attrs).map((attr) => ({ [attr]: node.attrs[attr] })));
856
731
  }
857
732
  };
858
733
  //#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);
734
+ //#region src/lib/rich-text-editor/rte/utils/textblock-marks.ts
735
+ var TextblockMarks = class {
736
+ constructor(specs) {
737
+ this.specs = specs;
873
738
  }
874
- /**
875
- * Returns the current document state.
876
- */
877
- getDocument() {
878
- return this[require_slottable_request.impl].state.doc.toJSON();
739
+ getAllowedMarksForNode(nodeName) {
740
+ return this.specs.filter((spec) => !spec.onNodeName || spec.onNodeName === nodeName).map((spec) => spec.markName);
879
741
  }
880
- /**
881
- * Reset the editor to its initial state. Optionally, an initial document can be provided.
882
- */
883
- reset(initialDocument) {
884
- this[require_slottable_request.impl].reset(initialDocument);
885
- }
886
- /**
887
- * Replaces the current selection with the given content.
888
- * If no text is selected, this inserts the content at the cursor position.
889
- */
890
- replaceSelection(content, options) {
891
- const { state, tr, schema, dispatchTransaction } = this[require_slottable_request.impl];
892
- tr.replaceSelection(new prosemirror_model.Slice(prosemirror_model.Fragment.fromJSON(schema, content), 0, 0));
893
- tr.doc.check();
894
- const $from = tr.doc.resolve(tr.mapping.map(state.selection.from));
895
- const $to = tr.doc.resolve(tr.selection.to);
896
- const [$head, $anchor] = options?.cursorPlacement === "start" ? [$from, $to] : [$to, $from];
897
- if (options?.selectContent) tr.setSelection(state.selection instanceof prosemirror_state.AllSelection ? new prosemirror_state.AllSelection(tr.doc) : prosemirror_state.TextSelection.between($anchor, $head));
898
- else tr.setSelection(prosemirror_state.TextSelection.between($head, $head));
899
- dispatchTransaction(tr);
900
- }
901
- /**
902
- * Replaces the entire document with the given new document.
903
- * Unlike reset, this preserves the rest of the editor state. The undo history is preserved, so the user can undo the replacement.
904
- */
905
- replaceDocument(newDocument, options) {
906
- const { state, tr, schema, dispatchTransaction } = this[require_slottable_request.impl];
907
- tr.replaceWith(0, state.doc.content.size, parseDocument(schema, newDocument));
908
- if (options?.selectContent) tr.setSelection(new prosemirror_state.AllSelection(tr.doc));
909
- else if (options?.cursorPlacement === "end") tr.setSelection(prosemirror_state.TextSelection.atEnd(tr.doc));
910
- else tr.setSelection(prosemirror_state.TextSelection.atStart(tr.doc));
911
- dispatchTransaction(tr);
742
+ getReferencedNodeNames() {
743
+ return new Set(this.specs.map((spec) => spec.onNodeName).filter(Boolean));
912
744
  }
913
745
  };
914
- var RteInstanceImpl = class {
915
- getFeature(name) {
916
- const f = this.config.featureMap.get(name);
917
- if (!f) throw new Error(`Feature not found: ${name}`);
918
- return f;
919
- }
920
- getPublicInterface(Feature, featureId) {
921
- const instances = this.config.featureFacadesMap.get(Feature);
922
- if (!instances || instances.length === 0) throw new Error(`Feature not found`);
923
- if (instances[0].featureId !== void 0) {
924
- if (featureId === void 0) throw new Error(`No featureId provided for multi-instance feature.`);
925
- const instance = instances.find((f) => f.featureId === featureId);
926
- if (!instance) throw new Error(`Feature with id "${featureId}" not found`);
927
- return instance.getPublicInterface(this);
746
+ //#endregion
747
+ //#region src/lib/rich-text-editor/rte/view.ts
748
+ /**
749
+ * Converts an RteDocument to an RteView tree for rendering outside the editor.
750
+ * The logic is adapted from ProseMirror's DOM serialization to ensure the resulting structure
751
+ * matches the editor's HTML structure.
752
+ */
753
+ var convertToView = (doc, ctx) => {
754
+ const schema = ctx.config[require_slottable_request.impl].schema;
755
+ const getMarkRank = (mark) => schema.marks[mark.type].rank;
756
+ const marksEqual = (a, b) => {
757
+ if (a.type !== b.type) return false;
758
+ const attrsA = a.attrs;
759
+ const attrsB = b.attrs;
760
+ if (attrsA === attrsB) return true;
761
+ /* v8 ignore next -- not currently reachable since all attrs are required @preserve */
762
+ if (!attrsA || !attrsB) return false;
763
+ const keysA = Object.keys(attrsA);
764
+ const keysB = Object.keys(attrsB);
765
+ /* v8 ignore next -- not currently reachable since all attrs are required @preserve */
766
+ if (keysA.length !== keysB.length) return false;
767
+ for (const key of keysA) if (attrsA[key] !== attrsB[key]) return false;
768
+ return true;
769
+ };
770
+ const convertFragment = (nodes) => {
771
+ const result = [];
772
+ const active = [];
773
+ const activeContents = [result];
774
+ for (const node of nodes) {
775
+ const nodeMarks = [...node.marks ?? []].sort((a, b) => getMarkRank(a) - getMarkRank(b));
776
+ let keep = 0;
777
+ let rendered = 0;
778
+ while (keep < active.length && rendered < nodeMarks.length) {
779
+ if (!marksEqual(active[keep], nodeMarks[rendered])) break;
780
+ keep++;
781
+ rendered++;
782
+ }
783
+ while (active.length > keep) {
784
+ active.pop();
785
+ activeContents.pop();
786
+ }
787
+ while (rendered < nodeMarks.length) {
788
+ const mark = nodeMarks[rendered];
789
+ const markContent = [];
790
+ activeContents[activeContents.length - 1].push({
791
+ type: "mark",
792
+ mark,
793
+ children: {
794
+ type: "fragment",
795
+ content: markContent,
796
+ [require_slottable_request.impl]: ctx
797
+ },
798
+ [require_slottable_request.impl]: ctx
799
+ });
800
+ active.push(mark);
801
+ activeContents.push(markContent);
802
+ rendered++;
803
+ }
804
+ activeContents[activeContents.length - 1].push({
805
+ type: "node",
806
+ node,
807
+ children: convertFragment(node.content ?? []),
808
+ [require_slottable_request.impl]: ctx
809
+ });
928
810
  }
929
- if (featureId !== void 0) throw new Error(`Feature does not support featureId`);
930
- return instances[0].getPublicInterface(this);
931
- }
932
- constructor(instanceFacade, configFacade, options) {
933
- this.options = options;
934
- this.view = null;
935
- this.dispatchTransaction = (tr) => {
936
- const prevState = this.state;
937
- this.state = this.state.apply(tr);
938
- this.view?.updateState(this.state);
939
- if (prevState.doc !== this.state.doc) this.options?.onChange?.();
940
- return this.state;
811
+ return {
812
+ type: "fragment",
813
+ content: result,
814
+ [require_slottable_request.impl]: ctx
941
815
  };
942
- this.facade = instanceFacade;
943
- const config = configFacade[require_slottable_request.impl];
944
- this.config = config;
945
- this.schema = config.schema;
946
- this.textblockAttrs = config.textblockAttrs;
947
- this.features = config.features;
948
- this.foreignHtmlParser = options?.foreignHtmlParser ?? new RteHtmlParser(configFacade);
949
- this.foreignHtmlSerializer = options?.foreignHtmlSerializer ?? new RteHtmlSerializer(configFacade);
950
- this.styles = new _microsoft_fast_element.ElementStyles(require_slottable_request.sortedContributions(config.features.flatMap((f) => f.getStyles())));
951
- this.initState(options?.initialDocument);
952
- }
953
- initState(initialDoc) {
954
- this.state = prosemirror_state.EditorState.create({
955
- doc: parseDocument(this.schema, initialDoc),
956
- schema: this.config.schema,
957
- plugins: require_slottable_request.sortedContributions(this.config.features.flatMap((feature) => feature.getPlugins(this)))
958
- });
816
+ };
817
+ return {
818
+ type: "node",
819
+ node: doc,
820
+ children: convertFragment(doc.content),
821
+ [require_slottable_request.impl]: ctx
822
+ };
823
+ };
824
+ //#endregion
825
+ //#region src/lib/rich-text-editor/rte/config.ts
826
+ var RteConfig = class {
827
+ constructor(features) {
828
+ this[require_slottable_request.impl] = new RteConfigImpl(features);
959
829
  }
960
- reset(initialDocument) {
961
- const currentHostState = hostBridgePlugin.getState(this.state);
962
- this.initState(initialDocument);
963
- this.updateHostState(currentHostState);
964
- this.view?.updateState(this.state);
830
+ instantiateEditor(options) {
831
+ return new RteInstance(this, options);
965
832
  }
966
- createView(target) {
967
- this.view = new prosemirror_view.EditorView((editor) => {
968
- editor.part = "editor";
969
- target.appendChild(editor);
970
- }, {
971
- state: this.state,
972
- dispatchTransaction: this.dispatchTransaction
833
+ instantiateView(document, options) {
834
+ return convertToView(document, {
835
+ config: this,
836
+ options: { ...options }
973
837
  });
974
838
  }
975
- destroyViewIfNeeded() {
976
- if (this.view) {
977
- this.view.destroy();
978
- this.view = null;
839
+ };
840
+ var RteConfigImpl = class {
841
+ constructor(featuresFacades) {
842
+ const resolveFeatures = (features) => features.flatMap((f) => f.getFeatures().flatMap((subFeature) => subFeature === f ? f : resolveFeatures([subFeature])));
843
+ this.features = resolveFeatures(featuresFacades.map(require_slottable_request.getFeatureImpl));
844
+ this.featureMap = /* @__PURE__ */ new Map();
845
+ for (const f of this.features) {
846
+ if (this.featureMap.has(f.name)) throw new Error(`Duplicate feature: ${f.name}`);
847
+ this.featureMap.set(f.name, f);
979
848
  }
849
+ this.featureFacadesMap = /* @__PURE__ */ new Map();
850
+ for (const facade of featuresFacades) {
851
+ const FacadeClass = facade.constructor;
852
+ const feature = require_slottable_request.getFeatureImpl(facade);
853
+ const instances = this.featureFacadesMap.get(FacadeClass);
854
+ if (instances) instances.push(feature);
855
+ else this.featureFacadesMap.set(FacadeClass, [feature]);
856
+ }
857
+ if (!this.featureMap.has("RteBase")) throw new Error("RteBase feature is required");
858
+ if (this.featureMap.has("RteLinkFeature") && !this.featureMap.has("RteToolbarFeature")) throw new Error("RteToolbarFeature is required for RteLinkFeature");
859
+ this.textblockAttrs = new TextblockAttrs(require_slottable_request.sortedContributions(this.features.flatMap((f) => f.getTextblockAttrs())));
860
+ this.textblockMarks = new TextblockMarks(require_slottable_request.sortedContributions(this.features.flatMap((f) => f.getTextblockMarks())));
861
+ const schemaContributions = require_slottable_request.sortedContributions(this.features.flatMap((f) => f.getSchema(this.textblockAttrs, this.textblockMarks)));
862
+ const schemaSpec = {
863
+ nodes: {},
864
+ marks: {}
865
+ };
866
+ for (const schema of schemaContributions) {
867
+ Object.assign(schemaSpec.nodes, schema.nodes ?? {});
868
+ Object.assign(schemaSpec.marks, schema.marks ?? {});
869
+ }
870
+ for (const referencedNodeName of this.textblockMarks.getReferencedNodeNames()) if (!(referencedNodeName in schemaSpec.nodes)) throw new Error(`Unknown node "${referencedNodeName}"`);
871
+ this.schema = new prosemirror_model.Schema(schemaSpec);
980
872
  }
981
- createComponent(type) {
982
- return document.createElement(this.hostState().ctx.tagFor(type, true));
873
+ };
874
+ //#endregion
875
+ //#region src/lib/rich-text-editor/rte/utils/ui.ts
876
+ var isPropBinding = (prop) => typeof prop === "function";
877
+ var on = (event, prop, handler) => [
878
+ event,
879
+ prop,
880
+ handler
881
+ ];
882
+ var UiCtx = class {
883
+ constructor(view, rte, props) {
884
+ this.view = view;
885
+ this.rte = rte;
886
+ this.props = props;
887
+ this.bindings = [];
983
888
  }
984
- getLocale() {
985
- return this.hostState().locale;
889
+ evalProp(prop) {
890
+ return isPropBinding(prop) ? prop(this) : prop;
986
891
  }
987
- get tr() {
988
- return this.state.tr;
892
+ bindProp(prop, bindFn) {
893
+ if (prop === void 0) return;
894
+ else if (isPropBinding(prop)) {
895
+ const binding = () => bindFn(prop(this));
896
+ this.bindings.push(binding);
897
+ binding();
898
+ } else bindFn(prop);
989
899
  }
990
- hostState() {
991
- const state = hostBridgePlugin.getState(this.state);
992
- if (!state) throw new Error("No host state available");
993
- return state;
900
+ updateBindings() {
901
+ for (const binding of this.bindings) binding();
994
902
  }
995
- updateHostState(hostState) {
996
- this.dispatchTransaction(this.tr.setMeta(hostBridgePlugin, hostState));
903
+ shouldReturnFocusToEditor() {
904
+ return this.evalProp(this.props.shouldReturnFocusToEditor);
905
+ }
906
+ bindToEl(target, props = {}, events = [], children = []) {
907
+ for (const name in props) this.bindProp(props[name], (value) => {
908
+ target[name] = value;
909
+ });
910
+ for (const [name, value, bindFn] of events) if (value) target.addEventListener(name, bindFn(value));
911
+ for (const child of children) target.appendChild(child);
912
+ }
913
+ };
914
+ var createDiv = (ctx, props) => {
915
+ const div = document.createElement("div");
916
+ ctx.bindToEl(div, {
917
+ className: props.className,
918
+ slot: props.slot
919
+ }, [], props.children);
920
+ return div;
921
+ };
922
+ var createAnchor = (ctx, props) => {
923
+ const anchor = document.createElement("a");
924
+ ctx.bindToEl(anchor, {
925
+ href: props.href,
926
+ target: props.target,
927
+ rel: props.rel,
928
+ className: props.className
929
+ }, [], props.children);
930
+ return anchor;
931
+ };
932
+ var wrapperTargets = /* @__PURE__ */ new WeakMap();
933
+ var createOptionalTooltip = (ctx, props) => {
934
+ const tooltip = ctx.rte.createComponent(require_definition$6.Tooltip);
935
+ tooltip.setAttribute("exportparts", "vvd-theme-alternate");
936
+ ctx.bindToEl(tooltip, {
937
+ className: "ui-tooltip",
938
+ text: props.label,
939
+ placement: ctx.props.popupPlacement
940
+ });
941
+ ctx.bindProp(props.enabled, (enabled) => {
942
+ if (!enabled) tooltip.open = false;
943
+ tooltip.anchor = enabled ? props.anchor : void 0;
944
+ });
945
+ const wrapper = createDiv(ctx, {
946
+ className: "ui-tooltip-wrapper",
947
+ slot: props.slot,
948
+ children: [props.anchor, tooltip]
949
+ });
950
+ wrapperTargets.set(wrapper, props.anchor);
951
+ return wrapper;
952
+ };
953
+ var createButton = (ctx, props) => {
954
+ const variant = () => ctx.evalProp(props.variant) ?? "toolbar";
955
+ const size = () => variant() === "toolbar" ? "super-condensed" : "condensed";
956
+ const appearanceInactive = () => variant() === "popover-primary" ? "outlined" : "ghost-light";
957
+ const appearanceActive = () => variant() === "toolbar-menu" || variant() === "popover" ? "filled" : appearanceInactive();
958
+ const appearance = () => ctx.evalProp(props.active) ? appearanceActive() : appearanceInactive();
959
+ const connotation = () => ctx.evalProp(props.connotation) ?? (variant() === "toolbar-menu" && ctx.evalProp(props.active) ? "cta" : void 0);
960
+ const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled) || ctx.evalProp(props.disabled));
961
+ const button = ctx.rte.createComponent(require_definition$1.Button);
962
+ ctx.bindToEl(button, {
963
+ size,
964
+ appearance,
965
+ disabled,
966
+ icon: props.icon,
967
+ autofocus: props.autofocus,
968
+ connotation,
969
+ [props.icon ? "ariaLabel" : "label"]: props.label,
970
+ pressed: () => Boolean(ctx.evalProp(props.active)),
971
+ ariaPressed: props.active !== void 0 ? () => ctx.evalProp(props.active) ? "true" : "false" : void 0
972
+ }, [on("click", props.onClick, (onClick) => () => {
973
+ if (!onClick() && ctx.shouldReturnFocusToEditor()) ctx.view.focus();
974
+ })]);
975
+ return createOptionalTooltip(ctx, {
976
+ enabled: () => Boolean(ctx.evalProp(props.icon) && !ctx.evalProp(props.noTooltip) && !disabled()),
977
+ label: props.label,
978
+ anchor: button,
979
+ slot: props.slot
980
+ });
981
+ };
982
+ var createMenu = (ctx, props) => {
983
+ const menu = ctx.rte.createComponent(require_definition$4.Menu);
984
+ ctx.bindToEl(menu, {
985
+ className: "ui-menu",
986
+ autoDismiss: true,
987
+ ariaLabel: props.label,
988
+ anchor: wrapperTargets.get(props.trigger) ?? props.trigger,
989
+ placement: ctx.props.popupPlacement,
990
+ offset: () => ctx.evalProp(ctx.props.menuOffset) ?? null
991
+ }, [], props.children);
992
+ const fragment = document.createDocumentFragment();
993
+ fragment.appendChild(props.trigger);
994
+ fragment.appendChild(menu);
995
+ return fragment;
996
+ };
997
+ var createMenuItem = (ctx, props) => {
998
+ const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled) || ctx.evalProp(props.disabled));
999
+ const item = ctx.rte.createComponent(require_definition$3.MenuItem);
1000
+ ctx.bindToEl(item, {
1001
+ text: props.text,
1002
+ checked: props.checked,
1003
+ disabled,
1004
+ controlType: "radio",
1005
+ checkedAppearance: "tick-only"
1006
+ }, [on("change", props.onSelect, (onSelect) => () => {
1007
+ if (item.checked && !item.disabled) {
1008
+ if (ctx.evalProp(props.checked) !== item.checked) {
1009
+ onSelect();
1010
+ if (ctx.shouldReturnFocusToEditor()) ctx.view.focus();
1011
+ }
1012
+ }
1013
+ })]);
1014
+ return item;
1015
+ };
1016
+ var createButtonGroup = (ctx, props) => createDiv(ctx, {
1017
+ slot: props.slot,
1018
+ className: "ui-button-group",
1019
+ children: props.children
1020
+ });
1021
+ function markActive(state, type) {
1022
+ const { from, $from, to, empty } = state.selection;
1023
+ if (empty) return !!type.isInSet(state.storedMarks || $from.marks());
1024
+ else return state.doc.rangeHasMark(from, to, type);
1025
+ }
1026
+ var createMarkToggle = (ctx, props) => createButton(ctx, {
1027
+ label: props.label,
1028
+ icon: props.icon,
1029
+ active: () => markActive(ctx.view.state, props.markType),
1030
+ disabled: () => !(0, prosemirror_commands.toggleMark)(props.markType)(ctx.view.state),
1031
+ onClick: () => {
1032
+ (0, prosemirror_commands.toggleMark)(props.markType)(ctx.view.state, ctx.view.dispatch);
997
1033
  }
1034
+ });
1035
+ var createOption = (ctx, props) => {
1036
+ const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled) || ctx.evalProp(props.disabled));
1037
+ const option = ctx.rte.createComponent(require_definition$7.ListboxOption);
1038
+ ctx.bindToEl(option, {
1039
+ value: props.value,
1040
+ text: props.text,
1041
+ disabled
1042
+ });
1043
+ return option;
1044
+ };
1045
+ var createSelect = (ctx, props) => {
1046
+ const disabled = () => Boolean(ctx.evalProp(ctx.props.disabled));
1047
+ const select = ctx.rte.createComponent(require_definition$9.Select);
1048
+ select.setAttribute("data-class", "ui-select");
1049
+ ctx.bindToEl(select, {
1050
+ placeholder: " ",
1051
+ ariaLabel: props.label,
1052
+ appearance: "ghost",
1053
+ disabled
1054
+ }, [on("change", props.onSelect, (onSelect) => () => {
1055
+ const value = select.value;
1056
+ if (value) {
1057
+ onSelect(value);
1058
+ if (ctx.shouldReturnFocusToEditor()) ctx.view.focus();
1059
+ }
1060
+ })], props.children);
1061
+ queueMicrotask(() => {
1062
+ ctx.bindProp(props.value, (value) => select.value = value);
1063
+ });
1064
+ let hideTooltip = false;
1065
+ select.addEventListener("vwc-popup:open", () => {
1066
+ hideTooltip = true;
1067
+ ctx.updateBindings();
1068
+ });
1069
+ select.addEventListener("vwc-popup:close", () => {
1070
+ hideTooltip = false;
1071
+ ctx.updateBindings();
1072
+ });
1073
+ return createOptionalTooltip(ctx, {
1074
+ enabled: () => !hideTooltip && !disabled(),
1075
+ label: props.label,
1076
+ anchor: select
1077
+ });
1078
+ };
1079
+ var createDivider = (ctx) => {
1080
+ const divider = ctx.rte.createComponent(require_divider.Divider);
1081
+ ctx.bindToEl(divider, {
1082
+ className: "ui-divider",
1083
+ orientation: "vertical"
1084
+ });
1085
+ return divider;
998
1086
  };
999
- //#endregion
1000
- //#region src/lib/rich-text-editor/rte/utils/textblock-attrs.ts
1001
- var TextblockAttrs = class {
1002
- constructor(specs) {
1003
- this.specs = specs;
1004
- this.attrs = {};
1005
- for (const spec of specs) this.attrs[spec.name] = { default: spec.default };
1006
- }
1007
- fromDOM(dom) {
1008
- return Object.assign({}, ...this.specs.map((s) => ({ [s.name]: s.fromDOM(dom) })));
1009
- }
1010
- getStyle(node) {
1011
- return this.specs.flatMap((s) => s.toStyles(node)).join("; ");
1012
- }
1013
- getDOMAttrsProperties(node) {
1014
- if (!this.specs.length) return {};
1015
- return { style: this.getStyle(node) };
1016
- }
1017
- getDOMAttrs(node) {
1018
- if (!this.specs.length) return [];
1019
- return [this.getDOMAttrsProperties(node)];
1020
- }
1021
- extractFromNode(node) {
1022
- return Object.assign({}, ...Object.keys(this.attrs).map((attr) => ({ [attr]: node.attrs[attr] })));
1023
- }
1087
+ var createTextField = (ctx, props) => {
1088
+ const textField = ctx.rte.createComponent(require_definition$5.TextField);
1089
+ ctx.bindToEl(textField, {
1090
+ label: props.label,
1091
+ value: props.value,
1092
+ placeholder: props.placeholder,
1093
+ slot: props.slot,
1094
+ autofocus: props.autofocus,
1095
+ type: props.type,
1096
+ helperText: props.helperText
1097
+ }, [on("input", props.onInput, (onInput) => () => {
1098
+ onInput(textField.value);
1099
+ })]);
1100
+ return textField;
1024
1101
  };
1025
- //#endregion
1026
- //#region src/lib/rich-text-editor/rte/utils/textblock-marks.ts
1027
- var TextblockMarks = class {
1028
- constructor(specs) {
1029
- this.specs = specs;
1030
- }
1031
- getAllowedMarksForNode(nodeName) {
1032
- return this.specs.filter((spec) => !spec.onNodeName || spec.onNodeName === nodeName).map((spec) => spec.markName);
1033
- }
1034
- getReferencedNodeNames() {
1035
- return new Set(this.specs.map((spec) => spec.onNodeName).filter(Boolean));
1036
- }
1102
+ var createText = (ctx, props) => {
1103
+ const textNode = document.createTextNode("");
1104
+ ctx.bindToEl(textNode, { textContent: props.text });
1105
+ return textNode;
1037
1106
  };
1038
- //#endregion
1039
- //#region src/lib/rich-text-editor/rte/view.ts
1040
- /**
1041
- * Converts an RteDocument to an RteView tree for rendering outside the editor.
1042
- * The logic is adapted from ProseMirror's DOM serialization to ensure the resulting structure
1043
- * matches the editor's HTML structure.
1044
- */
1045
- var convertToView = (doc, ctx) => {
1046
- const schema = ctx.config[require_slottable_request.impl].schema;
1047
- const getMarkRank = (mark) => schema.marks[mark.type].rank;
1048
- const marksEqual = (a, b) => {
1049
- if (a.type !== b.type) return false;
1050
- const attrsA = a.attrs;
1051
- const attrsB = b.attrs;
1052
- if (attrsA === attrsB) return true;
1053
- /* v8 ignore next -- not currently reachable since all attrs are required @preserve */
1054
- if (!attrsA || !attrsB) return false;
1055
- const keysA = Object.keys(attrsA);
1056
- const keysB = Object.keys(attrsB);
1057
- /* v8 ignore next -- not currently reachable since all attrs are required @preserve */
1058
- if (keysA.length !== keysB.length) return false;
1059
- for (const key of keysA) if (attrsA[key] !== attrsB[key]) return false;
1060
- return true;
1061
- };
1062
- const convertFragment = (nodes) => {
1063
- const result = [];
1064
- const active = [];
1065
- const activeContents = [result];
1066
- for (const node of nodes) {
1067
- const nodeMarks = [...node.marks ?? []].sort((a, b) => getMarkRank(a) - getMarkRank(b));
1068
- let keep = 0;
1069
- let rendered = 0;
1070
- while (keep < active.length && rendered < nodeMarks.length) {
1071
- if (!marksEqual(active[keep], nodeMarks[rendered])) break;
1072
- keep++;
1073
- rendered++;
1074
- }
1075
- while (active.length > keep) {
1076
- active.pop();
1077
- activeContents.pop();
1078
- }
1079
- while (rendered < nodeMarks.length) {
1080
- const mark = nodeMarks[rendered];
1081
- const markContent = [];
1082
- activeContents[activeContents.length - 1].push({
1083
- type: "mark",
1084
- mark,
1085
- children: {
1086
- type: "fragment",
1087
- content: markContent,
1088
- [require_slottable_request.impl]: ctx
1089
- },
1090
- [require_slottable_request.impl]: ctx
1091
- });
1092
- active.push(mark);
1093
- activeContents.push(markContent);
1094
- rendered++;
1095
- }
1096
- activeContents[activeContents.length - 1].push({
1097
- type: "node",
1098
- node,
1099
- children: convertFragment(node.content ?? []),
1100
- [require_slottable_request.impl]: ctx
1107
+ var createSingleSlot = (ctx, props) => {
1108
+ const slot = document.createElement("slot");
1109
+ slot.name = props.name;
1110
+ let currentEl = null;
1111
+ const listeners = [];
1112
+ function cleanup() {
1113
+ for (const { el, type, handler } of listeners) el.removeEventListener(type, handler);
1114
+ listeners.length = 0;
1115
+ currentEl = null;
1116
+ }
1117
+ for (const [key, prop] of Object.entries(props.assignedProps)) ctx.bindProp(prop, (value) => {
1118
+ if (currentEl) currentEl[key] = value;
1119
+ });
1120
+ function applyPropsAndEvents(el) {
1121
+ for (const [key, prop] of Object.entries(props.assignedProps)) el[key] = ctx.evalProp(prop);
1122
+ for (const [type, handler] of Object.entries(props.assignedEvents)) {
1123
+ const listener = (e) => {
1124
+ handler(e);
1125
+ if (ctx.shouldReturnFocusToEditor()) ctx.view.focus();
1126
+ };
1127
+ el.addEventListener(type, listener);
1128
+ listeners.push({
1129
+ el,
1130
+ type,
1131
+ handler: listener
1101
1132
  });
1102
1133
  }
1103
- return {
1104
- type: "fragment",
1105
- content: result,
1106
- [require_slottable_request.impl]: ctx
1107
- };
1108
- };
1109
- return {
1110
- type: "node",
1111
- node: doc,
1112
- children: convertFragment(doc.content),
1113
- [require_slottable_request.impl]: ctx
1134
+ }
1135
+ const processSlot = () => {
1136
+ const first = slot.assignedElements({ flatten: true })[0];
1137
+ if (first === currentEl) return;
1138
+ cleanup();
1139
+ if (first) {
1140
+ currentEl = first;
1141
+ applyPropsAndEvents(first);
1142
+ }
1114
1143
  };
1144
+ slot.addEventListener("slotchange", processSlot);
1145
+ queueMicrotask(processSlot);
1146
+ return slot;
1115
1147
  };
1116
1148
  //#endregion
1117
- //#region src/lib/rich-text-editor/rte/config.ts
1118
- var RteConfig = class {
1119
- constructor(features) {
1120
- this[require_slottable_request.impl] = new RteConfigImpl(features);
1121
- }
1122
- instantiateEditor(options) {
1123
- return new RteInstance(this, options);
1149
+ //#region src/lib/rich-text-editor/rte/features/internal/history.ts
1150
+ var RteHistoryFeatureImpl = class extends require_slottable_request.RteFeatureImpl {
1151
+ constructor(config) {
1152
+ super();
1153
+ this.config = config;
1154
+ this.name = "RteHistoryFeature";
1124
1155
  }
1125
- instantiateView(document, options) {
1126
- return convertToView(document, {
1127
- config: this,
1128
- options: { ...options }
1129
- });
1156
+ getPlugins() {
1157
+ return [this.contribution((0, prosemirror_history.history)()), this.contribution((0, prosemirror_keymap.keymap)({
1158
+ "Mod-z": prosemirror_history.undo,
1159
+ "Ctrl-y": prosemirror_history.redo,
1160
+ "Cmd-Z": prosemirror_history.redo
1161
+ }))];
1130
1162
  }
1131
- };
1132
- var RteConfigImpl = class {
1133
- constructor(featuresFacades) {
1134
- const resolveFeatures = (features) => features.flatMap((f) => f.getFeatures().flatMap((subFeature) => subFeature === f ? f : resolveFeatures([subFeature])));
1135
- this.features = resolveFeatures(featuresFacades.map(require_slottable_request.getFeatureImpl));
1136
- this.featureMap = /* @__PURE__ */ new Map();
1137
- for (const f of this.features) {
1138
- if (this.featureMap.has(f.name)) throw new Error(`Duplicate feature: ${f.name}`);
1139
- this.featureMap.set(f.name, f);
1140
- }
1141
- this.featureFacadesMap = /* @__PURE__ */ new Map();
1142
- for (const facade of featuresFacades) {
1143
- const FacadeClass = facade.constructor;
1144
- const feature = require_slottable_request.getFeatureImpl(facade);
1145
- const instances = this.featureFacadesMap.get(FacadeClass);
1146
- if (instances) instances.push(feature);
1147
- else this.featureFacadesMap.set(FacadeClass, [feature]);
1148
- }
1149
- if (!this.featureMap.has("RteBase")) throw new Error("RteBase feature is required");
1150
- if (this.featureMap.has("RteLinkFeature") && !this.featureMap.has("RteToolbarFeature")) throw new Error("RteToolbarFeature is required for RteLinkFeature");
1151
- this.textblockAttrs = new TextblockAttrs(require_slottable_request.sortedContributions(this.features.flatMap((f) => f.getTextblockAttrs())));
1152
- this.textblockMarks = new TextblockMarks(require_slottable_request.sortedContributions(this.features.flatMap((f) => f.getTextblockMarks())));
1153
- const schemaContributions = require_slottable_request.sortedContributions(this.features.flatMap((f) => f.getSchema(this.textblockAttrs, this.textblockMarks)));
1154
- const schemaSpec = {
1155
- nodes: {},
1156
- marks: {}
1157
- };
1158
- for (const schema of schemaContributions) {
1159
- Object.assign(schemaSpec.nodes, schema.nodes ?? {});
1160
- Object.assign(schemaSpec.marks, schema.marks ?? {});
1161
- }
1162
- for (const referencedNodeName of this.textblockMarks.getReferencedNodeNames()) if (!(referencedNodeName in schemaSpec.nodes)) throw new Error(`Unknown node "${referencedNodeName}"`);
1163
- this.schema = new prosemirror_model.Schema(schemaSpec);
1163
+ getToolbarItems() {
1164
+ if (!this.config.showToolbarButtons) return [];
1165
+ return [this.contribution({
1166
+ section: "history",
1167
+ render: (ctx) => createButton(ctx, {
1168
+ label: (ctx) => ctx.rte.getLocale().richTextEditor.undo,
1169
+ icon: "undo-line",
1170
+ disabled: (ctx) => !(0, prosemirror_history.undo)(ctx.view.state),
1171
+ onClick: () => {
1172
+ const { state, dispatch } = ctx.view;
1173
+ (0, prosemirror_history.undo)(state, dispatch);
1174
+ }
1175
+ })
1176
+ }, 1), this.contribution({
1177
+ section: "history",
1178
+ render: (ctx) => createButton(ctx, {
1179
+ label: (ctx) => ctx.rte.getLocale().richTextEditor.redo,
1180
+ icon: "redo-line",
1181
+ disabled: (ctx) => !(0, prosemirror_history.redo)(ctx.view.state),
1182
+ onClick: () => {
1183
+ const { state, dispatch } = ctx.view;
1184
+ (0, prosemirror_history.redo)(state, dispatch);
1185
+ }
1186
+ })
1187
+ }, 2)];
1164
1188
  }
1165
1189
  };
1166
1190
  //#endregion
@@ -1346,6 +1370,7 @@ var RteBaseImpl = class extends require_slottable_request.RteFeatureImpl {
1346
1370
  return [
1347
1371
  this,
1348
1372
  new RteCoreImpl(),
1373
+ new RteHistoryFeatureImpl({ showToolbarButtons: this.config?.historyToolbarButtons ?? true }),
1349
1374
  new RteBasicTextBlocksImpl({
1350
1375
  heading1: this.config?.heading1 ?? false,
1351
1376
  heading2: this.config?.heading2 ?? false,
@@ -1363,6 +1388,20 @@ var RteBaseImpl = class extends require_slottable_request.RteFeatureImpl {
1363
1388
  },
1364
1389
  set disabled(value) {
1365
1390
  core.disabled.setValue(rte, value);
1391
+ },
1392
+ get scrollThreshold() {
1393
+ return core.scrollThreshold;
1394
+ },
1395
+ set scrollThreshold(value) {
1396
+ core.scrollThreshold = value;
1397
+ rte.view?.setProps({ scrollThreshold: value });
1398
+ },
1399
+ get scrollMargin() {
1400
+ return core.scrollMargin;
1401
+ },
1402
+ set scrollMargin(value) {
1403
+ core.scrollMargin = value;
1404
+ rte.view?.setProps({ scrollMargin: value });
1366
1405
  }
1367
1406
  };
1368
1407
  }
@@ -1953,7 +1992,7 @@ var RteTextColorPickerFeatureImpl = class extends require_slottable_request.RteF
1953
1992
  if (selectionColor === null) selectionColor = observedColor ?? null;
1954
1993
  else if (observedColor !== selectionColor) selectionColor = mixedColor;
1955
1994
  };
1956
- state.doc.nodesBetween(from, to, (node, pos, parent) => {
1995
+ state.doc.nodesBetween(from, to, (node, _pos, parent) => {
1957
1996
  if (selectionColor === mixedColor) return false;
1958
1997
  if (!node.isLeaf) {
1959
1998
  if (node.type.allowsMarkType(textColor) && node.childCount === 0) observeColor(this.defaultColorForNode?.[node.type.name]);
@@ -2702,7 +2741,7 @@ var InlineImageView = class {
2702
2741
  this.img.className = "inline-image";
2703
2742
  this.update(node);
2704
2743
  this.img.addEventListener("load", () => {
2705
- const pos = this.getPos?.();
2744
+ const pos = this.getPos();
2706
2745
  if (pos) {
2707
2746
  const { state, dispatch } = this.view;
2708
2747
  const currentNode = state.doc.nodeAt(pos);
@@ -2809,6 +2848,7 @@ var RteInlineImageFeatureImpl = class extends require_slottable_request.RteFeatu
2809
2848
  shouldReturnFocusToEditor: true
2810
2849
  });
2811
2850
  popover = rte.createComponent(Popover);
2851
+ popover.kind = "imageOptions";
2812
2852
  popover.offset = 4;
2813
2853
  const content = createDiv(ctx, {
2814
2854
  className: "inline-image-popover",
@@ -2869,7 +2909,7 @@ var RteInlineImageFeatureImpl = class extends require_slottable_request.RteFeatu
2869
2909
  } }))];
2870
2910
  }
2871
2911
  calculateSmallWidth(naturalWidth) {
2872
- return Math.min(300, naturalWidth / 2) + "px";
2912
+ return `${Math.min(300, naturalWidth / 2)}px`;
2873
2913
  }
2874
2914
  getSelectedInlineImage(state) {
2875
2915
  const sel = state.selection;
@@ -3002,13 +3042,13 @@ var RteFileHandlerFeatureImpl = class extends require_slottable_request.RteFeatu
3002
3042
  if (resolved.type === "error") console.error("Error in handleFiles handler:", resolved.error);
3003
3043
  };
3004
3044
  return [this.contribution(insertPointPlaceholderPlugin), this.contribution(new prosemirror_state.Plugin({ props: {
3005
- handlePaste: (view, event) => {
3045
+ handlePaste: (_view, event) => {
3006
3046
  const files = Array.from(event.clipboardData.files);
3007
3047
  if (!files.length) return false;
3008
3048
  handleFiles(files, { type: "selection" });
3009
3049
  return true;
3010
3050
  },
3011
- handleDrop: (view, event, slice, moved) => {
3051
+ handleDrop: (view, event) => {
3012
3052
  const files = Array.from(event.dataTransfer.files);
3013
3053
  if (!files.length) return false;
3014
3054
  let target = view.posAtCoords({
@@ -3037,7 +3077,7 @@ var RteDropHandlerFeatureImpl = class extends require_slottable_request.RteFeatu
3037
3077
  this.config = config;
3038
3078
  this.name = "RteDebugFeature";
3039
3079
  }
3040
- getPlugins(rte) {
3080
+ getPlugins() {
3041
3081
  const dragOverResults = /* @__PURE__ */ new WeakMap();
3042
3082
  let lastResult = false;
3043
3083
  const onDragOver = (event) => {
@@ -3075,7 +3115,7 @@ var RteDropHandlerFeatureImpl = class extends require_slottable_request.RteFeatu
3075
3115
  viewport.removeEventListener("dragleave", onDragLeave);
3076
3116
  } };
3077
3117
  } })), this.contribution(new prosemirror_state.Plugin({ props: { handleDOMEvents: {
3078
- dragover: (view, event) => {
3118
+ dragover: (_view, event) => {
3079
3119
  const result = this.config.onViewportDragOver?.(event) ?? false;
3080
3120
  dragOverResults.set(event, result);
3081
3121
  lastResult = result;
@@ -3086,7 +3126,7 @@ var RteDropHandlerFeatureImpl = class extends require_slottable_request.RteFeatu
3086
3126
  }
3087
3127
  return false;
3088
3128
  },
3089
- drop: (view, event) => {
3129
+ drop: () => {
3090
3130
  return lastResult;
3091
3131
  }
3092
3132
  } } }), require_slottable_request.contributionPriority.high)];
@@ -3225,7 +3265,6 @@ var RteInputRuleFeature = require_slottable_request.featureFacade(RteInputRuleFe
3225
3265
  //#endregion
3226
3266
  //#region src/lib/rich-text-editor/rte/features/keyboard-shortcuts.ts
3227
3267
  function toCommand(handler, rteInstance) {
3228
- if (handler.length === 0) return (_state, _dispatch) => handler();
3229
3268
  return (_state, _dispatch) => handler(rteInstance);
3230
3269
  }
3231
3270
  var RteKeyboardShortcutsFeatureImpl = class extends require_slottable_request.RteFeatureImpl {