@commercetools-uikit/rich-text-utils 19.24.0 → 19.25.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.
@@ -12,9 +12,11 @@ var utils = require('@commercetools-uikit/utils');
12
12
  var uniq = require('lodash/uniq');
13
13
  var _defineProperty = require('@babel/runtime-corejs3/helpers/defineProperty');
14
14
  var _mapInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/map');
15
+ var _trimInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/trim');
16
+ var _startsWithInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/starts-with');
15
17
  var _Array$isArray = require('@babel/runtime-corejs3/core-js-stable/array/is-array');
18
+ var _Array$from = require('@babel/runtime-corejs3/core-js-stable/array/from');
16
19
  var _flatInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/flat');
17
- var _Array$from3 = require('@babel/runtime-corejs3/core-js-stable/array/from');
18
20
  var _forEachInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/for-each');
19
21
  var _Object$getOwnPropertySymbols = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols');
20
22
  var _filterInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/filter');
@@ -28,11 +30,12 @@ var slateHyperscript = require('slate-hyperscript');
28
30
  var parse = require('style-to-object');
29
31
  var isEmpty$2 = require('lodash/isEmpty');
30
32
  var _includesInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/includes');
33
+ var react = require('react');
31
34
  var slateReact = require('slate-react');
35
+ var isUrl = require('is-url');
32
36
  var jsxRuntime = require('@emotion/react/jsx-runtime');
33
37
  var _someInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/some');
34
38
  var _pt = require('prop-types');
35
- var react = require('react');
36
39
  var inputUtils = require('@commercetools-uikit/input-utils');
37
40
  var _objectWithoutProperties = require('@babel/runtime-corejs3/helpers/objectWithoutProperties');
38
41
  var _styled = require('@emotion/styled/base');
@@ -55,9 +58,11 @@ var _Object$entries__default = /*#__PURE__*/_interopDefault(_Object$entries);
55
58
  var _Object$keys__default = /*#__PURE__*/_interopDefault(_Object$keys);
56
59
  var uniq__default = /*#__PURE__*/_interopDefault(uniq);
57
60
  var _mapInstanceProperty__default = /*#__PURE__*/_interopDefault(_mapInstanceProperty);
61
+ var _trimInstanceProperty__default = /*#__PURE__*/_interopDefault(_trimInstanceProperty);
62
+ var _startsWithInstanceProperty__default = /*#__PURE__*/_interopDefault(_startsWithInstanceProperty);
58
63
  var _Array$isArray__default = /*#__PURE__*/_interopDefault(_Array$isArray);
64
+ var _Array$from__default = /*#__PURE__*/_interopDefault(_Array$from);
59
65
  var _flatInstanceProperty__default = /*#__PURE__*/_interopDefault(_flatInstanceProperty);
60
- var _Array$from3__default = /*#__PURE__*/_interopDefault(_Array$from3);
61
66
  var _forEachInstanceProperty__default = /*#__PURE__*/_interopDefault(_forEachInstanceProperty);
62
67
  var _Object$getOwnPropertySymbols__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertySymbols);
63
68
  var _filterInstanceProperty__default = /*#__PURE__*/_interopDefault(_filterInstanceProperty);
@@ -69,6 +74,7 @@ var escapeHtml__default = /*#__PURE__*/_interopDefault(escapeHtml);
69
74
  var parse__default = /*#__PURE__*/_interopDefault(parse);
70
75
  var isEmpty__default = /*#__PURE__*/_interopDefault(isEmpty$2);
71
76
  var _includesInstanceProperty__default = /*#__PURE__*/_interopDefault(_includesInstanceProperty);
77
+ var isUrl__default = /*#__PURE__*/_interopDefault(isUrl);
72
78
  var _someInstanceProperty__default = /*#__PURE__*/_interopDefault(_someInstanceProperty);
73
79
  var _pt__default = /*#__PURE__*/_interopDefault(_pt);
74
80
  var _styled__default = /*#__PURE__*/_interopDefault(_styled);
@@ -89,7 +95,8 @@ const BLOCK_TAGS = {
89
95
  pre: 'code',
90
96
  li: 'list-item',
91
97
  ol: 'numbered-list',
92
- ul: 'bulleted-list'
98
+ ul: 'bulleted-list',
99
+ a: 'link'
93
100
  };
94
101
 
95
102
  // Add a dictionary of mark tags.
@@ -173,6 +180,13 @@ const Element = _ref => {
173
180
  }, attributes), {}, {
174
181
  children: children
175
182
  }));
183
+ case BLOCK_TAGS.a:
184
+ return jsxRuntime.jsx("a", _objectSpread$g(_objectSpread$g({
185
+ style: style
186
+ }, attributes), {}, {
187
+ rel: "noopener noreferrer",
188
+ children: children
189
+ }));
176
190
  default:
177
191
  return jsxRuntime.jsx("p", _objectSpread$g(_objectSpread$g({
178
192
  style: style
@@ -259,7 +273,7 @@ const toggleMark = (editor, format) => {
259
273
  const isBlockActive = (editor, format) => {
260
274
  const selection = editor.selection;
261
275
  if (!selection) return false;
262
- const _Array$from = _Array$from3__default["default"](slate.Editor.nodes(editor, {
276
+ const _Array$from = _Array$from__default["default"](slate.Editor.nodes(editor, {
263
277
  at: slate.Editor.unhangRange(editor, selection),
264
278
  match: n => !slate.Editor.isEditor(n) && slate.Element.isElement(n) && n.type === format
265
279
  })),
@@ -362,9 +376,74 @@ const Softbreaker = {
362
376
  return html.split(this.placeholderCharacter).join('');
363
377
  }
364
378
  };
379
+ const isLinkActive = editor => {
380
+ const _Editor$nodes = slate.Editor.nodes(editor, {
381
+ match: n => !slate.Editor.isEditor(n) && slate.Element.isElement(n) && n.type === 'link'
382
+ }),
383
+ _Editor$nodes2 = _slicedToArray(_Editor$nodes, 1),
384
+ link = _Editor$nodes2[0];
385
+ return !!link;
386
+ };
387
+ const unwrapLink = editor => {
388
+ slate.Transforms.unwrapNodes(editor, {
389
+ match: n => !slate.Editor.isEditor(n) && slate.Element.isElement(n) && n.type === 'link'
390
+ });
391
+ };
392
+ const wrapLink = (editor, url) => {
393
+ if (isLinkActive(editor)) {
394
+ unwrapLink(editor);
395
+ }
396
+ const selection = editor.selection;
397
+ const isCollapsed = selection && slate.Range.isCollapsed(selection);
398
+ const linkNode = {
399
+ type: 'link',
400
+ url,
401
+ children: isCollapsed ? [{
402
+ text: url
403
+ }] : []
404
+ };
405
+ if (isCollapsed) {
406
+ slate.Transforms.insertNodes(editor, linkNode);
407
+ } else {
408
+ slate.Transforms.wrapNodes(editor, linkNode, {
409
+ split: true
410
+ });
411
+ slate.Transforms.collapse(editor, {
412
+ edge: 'end'
413
+ });
414
+ }
415
+ };
416
+ const withLinks = editor => {
417
+ const insertText = editor.insertText,
418
+ insertData = editor.insertData,
419
+ isInline = editor.isInline;
420
+
421
+ // Mark link elements as inline (from example)
422
+ editor.isInline = element => {
423
+ return element.type === 'link' || isInline(element);
424
+ };
425
+
426
+ // Handle URL pasting/typing to automatically create links (from example)
427
+ editor.insertText = text => {
428
+ if (text && isUrl__default["default"](text)) {
429
+ wrapLink(editor, text);
430
+ } else {
431
+ insertText(text);
432
+ }
433
+ };
434
+ editor.insertData = data => {
435
+ const text = data.getData('text/plain');
436
+ if (text && isUrl__default["default"](text)) {
437
+ wrapLink(editor, text);
438
+ } else {
439
+ insertData(data);
440
+ }
441
+ };
442
+ return editor;
443
+ };
365
444
 
366
445
  function ownKeys$f(e, r) { var t = _Object$keys__default["default"](e); if (_Object$getOwnPropertySymbols__default["default"]) { var o = _Object$getOwnPropertySymbols__default["default"](e); r && (o = _filterInstanceProperty__default["default"](o).call(o, function (r) { return _Object$getOwnPropertyDescriptor__default["default"](e, r).enumerable; })), t.push.apply(t, o); } return t; }
367
- function _objectSpread$f(e) { for (var r = 1; r < arguments.length; r++) { var _context5, _context6; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty__default["default"](_context5 = ownKeys$f(Object(t), !0)).call(_context5, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](e, _Object$getOwnPropertyDescriptors__default["default"](t)) : _forEachInstanceProperty__default["default"](_context6 = ownKeys$f(Object(t))).call(_context6, function (r) { _Object$defineProperty__default["default"](e, r, _Object$getOwnPropertyDescriptor__default["default"](t, r)); }); } return e; }
446
+ function _objectSpread$f(e) { for (var r = 1; r < arguments.length; r++) { var _context8, _context9; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty__default["default"](_context8 = ownKeys$f(Object(t), !0)).call(_context8, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](e, _Object$getOwnPropertyDescriptors__default["default"](t)) : _forEachInstanceProperty__default["default"](_context9 = ownKeys$f(Object(t))).call(_context9, function (r) { _Object$defineProperty__default["default"](e, r, _Object$getOwnPropertyDescriptor__default["default"](t, r)); }); } return e; }
368
447
 
369
448
  // Slate's way of providing custom type annotations comes down to extending `CustomTypes` interface
370
449
  // more: https://docs.slatejs.org/concepts/12-typescript
@@ -401,15 +480,16 @@ const serializeNode = node => {
401
480
  return string;
402
481
  }
403
482
  const children = _mapInstanceProperty__default["default"](_context = node.children).call(_context, serializeNode).join('');
404
- switch (node.type) {
483
+ const element = node; // Use updated CustomElement
484
+
485
+ switch (element.type) {
405
486
  case 'block-quote':
406
487
  return `<blockquote>${children}</blockquote>`;
407
488
  case 'paragraph':
408
489
  return `<p>${children}</p>`;
409
490
  case 'code':
410
491
  return `<pre>
411
- <code>${children}</code>
412
- </pre>`;
492
+ <code>${children}</code>`;
413
493
  case 'span':
414
494
  return `<span>${children}</span>`;
415
495
  case 'bulleted-list':
@@ -418,6 +498,36 @@ const serializeNode = node => {
418
498
  return `<ol>${children}</ol>`;
419
499
  case 'list-item':
420
500
  return `<li>${children}</li>`;
501
+ case BLOCK_TAGS.a:
502
+ // Handle link serialization
503
+ // eslint-disable-next-line no-case-declarations
504
+ let hrefAttr = '';
505
+ if (element.url) {
506
+ // Sanitize href to prevent javascript: URLs during serialization as well
507
+ const sanitizedUrl = (_context2 => {
508
+ const url = _trimInstanceProperty__default["default"](_context2 = String(element.url)).call(_context2).toLowerCase();
509
+ if (_startsWithInstanceProperty__default["default"](url).call(url, 'javascript:') || _startsWithInstanceProperty__default["default"](url).call(url, 'data:') || _startsWithInstanceProperty__default["default"](url).call(url, 'vbscript:')) {
510
+ return '#';
511
+ }
512
+ return String(element.url);
513
+ })();
514
+ hrefAttr = ` href="${escapeHtml__default["default"](sanitizedUrl)}"`;
515
+ }
516
+ // eslint-disable-next-line no-case-declarations
517
+ let otherAttrsString = '';
518
+ if (element.htmlAttributes && typeof element.htmlAttributes === 'object') {
519
+ for (const _ref of _Object$entries__default["default"](element.htmlAttributes)) {
520
+ var _context3;
521
+ var _ref2 = _slicedToArray(_ref, 2);
522
+ const key = _ref2[0];
523
+ const value = _ref2[1];
524
+ // Strip event handlers during serialization too
525
+ if (!_startsWithInstanceProperty__default["default"](_context3 = key.toLowerCase()).call(_context3, 'on')) {
526
+ otherAttrsString += ` ${escapeHtml__default["default"](key)}="${escapeHtml__default["default"](String(value))}"`;
527
+ }
528
+ }
529
+ }
530
+ return `<a${hrefAttr}${otherAttrsString}>${children}</a>`;
421
531
  case 'heading-one':
422
532
  return `<h1>${children}</h1>`;
423
533
  case 'heading-two':
@@ -425,7 +535,7 @@ const serializeNode = node => {
425
535
  case 'heading-three':
426
536
  return `<h3>${children}</h3>`;
427
537
  case 'heading-four':
428
- return `<h4}>${children}</h4>`;
538
+ return `<h4>${children}</h4>`;
429
539
  case 'heading-five':
430
540
  return `<h5>${children}</h5>`;
431
541
  default:
@@ -447,6 +557,35 @@ const serialize = value => {
447
557
  return outputHtml;
448
558
  };
449
559
  const ELEMENT_TAGS = {
560
+ A: el => {
561
+ const props = {
562
+ type: BLOCK_TAGS.a
563
+ };
564
+ const htmlAttributes = {};
565
+ for (const attr of _Array$from__default["default"](el.attributes)) {
566
+ const attrName = attr.name.toLowerCase();
567
+ const attrValue = attr.value;
568
+ if (attrName === 'href') {
569
+ var _context4;
570
+ // Sanitize href to prevent javascript: URLs
571
+ const sanitizedValue = _trimInstanceProperty__default["default"](_context4 = decodeURI(attrValue)).call(_context4).toLowerCase();
572
+ if (
573
+ // eslint-disable-next-line no-script-url
574
+ _startsWithInstanceProperty__default["default"](sanitizedValue).call(sanitizedValue, 'javascript:') || _startsWithInstanceProperty__default["default"](sanitizedValue).call(sanitizedValue, 'data:') || _startsWithInstanceProperty__default["default"](sanitizedValue).call(sanitizedValue, 'vbscript:')) {
575
+ props.url = '#'; // Replace with a safe value
576
+ } else {
577
+ props.url = attrValue;
578
+ }
579
+ } else if (!_startsWithInstanceProperty__default["default"](attrName).call(attrName, 'on')) {
580
+ // Strip event handlers
581
+ htmlAttributes[attrName] = attrValue;
582
+ }
583
+ }
584
+ if (_Object$keys__default["default"](htmlAttributes).length > 0) {
585
+ props.htmlAttributes = htmlAttributes;
586
+ }
587
+ return props;
588
+ },
450
589
  BLOCKQUOTE: () => ({
451
590
  type: 'quote'
452
591
  }),
@@ -552,7 +691,7 @@ const wrapWithParagraph = textContent => slateHyperscript.jsx('element', {
552
691
  const wrapWithParagraphIfRootElement = (el, textContent) => el.parentNode?.nodeName === 'BODY' // root element, because body is eventually turned to React fragment
553
692
  ? wrapWithParagraph(textContent) : textContent;
554
693
  const deserializeElement = el => {
555
- var _context2, _context3;
694
+ var _context5, _context6;
556
695
  // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType#value
557
696
  if (el.nodeType === 3) {
558
697
  return wrapWithParagraphIfRootElement(el, {
@@ -569,7 +708,7 @@ const deserializeElement = el => {
569
708
  if (nodeName === 'PRE' && el.childNodes[0] && el.childNodes[0].nodeName === 'CODE') {
570
709
  parent = el.childNodes[0];
571
710
  }
572
- let children = _flatInstanceProperty__default["default"](_context2 = _mapInstanceProperty__default["default"](_context3 = _Array$from3__default["default"](parent.childNodes)).call(_context3, deserializeElement)).call(_context2);
711
+ let children = _flatInstanceProperty__default["default"](_context5 = _mapInstanceProperty__default["default"](_context6 = _Array$from__default["default"](parent.childNodes)).call(_context6, deserializeElement)).call(_context5);
573
712
  if (children.length === 0) {
574
713
  children = [{
575
714
  text: ''
@@ -588,11 +727,11 @@ const deserializeElement = el => {
588
727
  type: 'span'
589
728
  }, children));
590
729
  } else {
591
- var _context4;
592
- attrs = _reduceInstanceProperty__default["default"](_context4 = _Object$entries__default["default"](styleObj || {})).call(_context4, (mappedAttrObj, _ref) => {
593
- let _ref2 = _slicedToArray(_ref, 2),
594
- key = _ref2[0],
595
- value = _ref2[1];
730
+ var _context7;
731
+ attrs = _reduceInstanceProperty__default["default"](_context7 = _Object$entries__default["default"](styleObj || {})).call(_context7, (mappedAttrObj, _ref3) => {
732
+ let _ref4 = _slicedToArray(_ref3, 2),
733
+ key = _ref4[0],
734
+ value = _ref4[1];
596
735
  const values = value.split(' '); // to cover the case of space-separated values e.g. `text-decoration-line: "underline line-through"`
597
736
 
598
737
  _forEachInstanceProperty__default["default"](values).call(values, splittedValue => {
@@ -614,8 +753,11 @@ const deserializeElement = el => {
614
753
  _mapInstanceProperty__default["default"](children).call(children, child => slate.Text.isText(child) ? slateHyperscript.jsx('text', attrs, child) : slateHyperscript.jsx('element', attrs, child)));
615
754
  }
616
755
  }
756
+
757
+ // Modified to use the updated ELEMENT_TAGS for 'A'
617
758
  if (ELEMENT_TAGS[nodeName]) {
618
- const attrs = ELEMENT_TAGS[nodeName]();
759
+ const attrs = ELEMENT_TAGS[nodeName](el // Pass element to access its attributes
760
+ );
619
761
  return slateHyperscript.jsx('element', attrs, children);
620
762
  }
621
763
  if (TEXT_TAGS[nodeName]) {
@@ -1838,7 +1980,7 @@ RichTextEditorBody.displayName = 'RichTextEditorBody';
1838
1980
  var RichTextEditorBody$1 = RichTextEditorBody;
1839
1981
 
1840
1982
  // NOTE: This string will be replaced on build time with the package version.
1841
- var version = "19.24.0";
1983
+ var version = "19.25.0";
1842
1984
 
1843
1985
  exports.Element = Element;
1844
1986
  exports.HiddenInput = HiddenInput$1;
@@ -1856,3 +1998,4 @@ exports.toggleBlock = toggleBlock;
1856
1998
  exports.toggleMark = toggleMark;
1857
1999
  exports.validSlateStateAdapter = validSlateStateAdapter;
1858
2000
  exports.version = version;
2001
+ exports.withLinks = withLinks;
@@ -12,9 +12,11 @@ require('@commercetools-uikit/utils');
12
12
  var uniq = require('lodash/uniq');
13
13
  var _defineProperty = require('@babel/runtime-corejs3/helpers/defineProperty');
14
14
  var _mapInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/map');
15
+ var _trimInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/trim');
16
+ var _startsWithInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/starts-with');
15
17
  var _Array$isArray = require('@babel/runtime-corejs3/core-js-stable/array/is-array');
18
+ var _Array$from = require('@babel/runtime-corejs3/core-js-stable/array/from');
16
19
  var _flatInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/flat');
17
- var _Array$from3 = require('@babel/runtime-corejs3/core-js-stable/array/from');
18
20
  var _forEachInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/for-each');
19
21
  var _Object$getOwnPropertySymbols = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols');
20
22
  var _filterInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/filter');
@@ -28,11 +30,12 @@ var slateHyperscript = require('slate-hyperscript');
28
30
  var parse = require('style-to-object');
29
31
  var isEmpty$2 = require('lodash/isEmpty');
30
32
  var _includesInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/includes');
33
+ var react = require('react');
31
34
  var slateReact = require('slate-react');
35
+ var isUrl = require('is-url');
32
36
  var jsxRuntime = require('@emotion/react/jsx-runtime');
33
37
  var _someInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/some');
34
38
  require('prop-types');
35
- var react = require('react');
36
39
  var inputUtils = require('@commercetools-uikit/input-utils');
37
40
  var _objectWithoutProperties = require('@babel/runtime-corejs3/helpers/objectWithoutProperties');
38
41
  var _styled = require('@emotion/styled/base');
@@ -55,9 +58,11 @@ var _Object$entries__default = /*#__PURE__*/_interopDefault(_Object$entries);
55
58
  var _Object$keys__default = /*#__PURE__*/_interopDefault(_Object$keys);
56
59
  var uniq__default = /*#__PURE__*/_interopDefault(uniq);
57
60
  var _mapInstanceProperty__default = /*#__PURE__*/_interopDefault(_mapInstanceProperty);
61
+ var _trimInstanceProperty__default = /*#__PURE__*/_interopDefault(_trimInstanceProperty);
62
+ var _startsWithInstanceProperty__default = /*#__PURE__*/_interopDefault(_startsWithInstanceProperty);
58
63
  var _Array$isArray__default = /*#__PURE__*/_interopDefault(_Array$isArray);
64
+ var _Array$from__default = /*#__PURE__*/_interopDefault(_Array$from);
59
65
  var _flatInstanceProperty__default = /*#__PURE__*/_interopDefault(_flatInstanceProperty);
60
- var _Array$from3__default = /*#__PURE__*/_interopDefault(_Array$from3);
61
66
  var _forEachInstanceProperty__default = /*#__PURE__*/_interopDefault(_forEachInstanceProperty);
62
67
  var _Object$getOwnPropertySymbols__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertySymbols);
63
68
  var _filterInstanceProperty__default = /*#__PURE__*/_interopDefault(_filterInstanceProperty);
@@ -69,6 +74,7 @@ var escapeHtml__default = /*#__PURE__*/_interopDefault(escapeHtml);
69
74
  var parse__default = /*#__PURE__*/_interopDefault(parse);
70
75
  var isEmpty__default = /*#__PURE__*/_interopDefault(isEmpty$2);
71
76
  var _includesInstanceProperty__default = /*#__PURE__*/_interopDefault(_includesInstanceProperty);
77
+ var isUrl__default = /*#__PURE__*/_interopDefault(isUrl);
72
78
  var _someInstanceProperty__default = /*#__PURE__*/_interopDefault(_someInstanceProperty);
73
79
  var _styled__default = /*#__PURE__*/_interopDefault(_styled);
74
80
  var _findInstanceProperty__default = /*#__PURE__*/_interopDefault(_findInstanceProperty);
@@ -88,7 +94,8 @@ const BLOCK_TAGS = {
88
94
  pre: 'code',
89
95
  li: 'list-item',
90
96
  ol: 'numbered-list',
91
- ul: 'bulleted-list'
97
+ ul: 'bulleted-list',
98
+ a: 'link'
92
99
  };
93
100
 
94
101
  // Add a dictionary of mark tags.
@@ -172,6 +179,13 @@ const Element = _ref => {
172
179
  }, attributes), {}, {
173
180
  children: children
174
181
  }));
182
+ case BLOCK_TAGS.a:
183
+ return jsxRuntime.jsx("a", _objectSpread$g(_objectSpread$g({
184
+ style: style
185
+ }, attributes), {}, {
186
+ rel: "noopener noreferrer",
187
+ children: children
188
+ }));
175
189
  default:
176
190
  return jsxRuntime.jsx("p", _objectSpread$g(_objectSpread$g({
177
191
  style: style
@@ -258,7 +272,7 @@ const toggleMark = (editor, format) => {
258
272
  const isBlockActive = (editor, format) => {
259
273
  const selection = editor.selection;
260
274
  if (!selection) return false;
261
- const _Array$from = _Array$from3__default["default"](slate.Editor.nodes(editor, {
275
+ const _Array$from = _Array$from__default["default"](slate.Editor.nodes(editor, {
262
276
  at: slate.Editor.unhangRange(editor, selection),
263
277
  match: n => !slate.Editor.isEditor(n) && slate.Element.isElement(n) && n.type === format
264
278
  })),
@@ -361,9 +375,74 @@ const Softbreaker = {
361
375
  return html.split(this.placeholderCharacter).join('');
362
376
  }
363
377
  };
378
+ const isLinkActive = editor => {
379
+ const _Editor$nodes = slate.Editor.nodes(editor, {
380
+ match: n => !slate.Editor.isEditor(n) && slate.Element.isElement(n) && n.type === 'link'
381
+ }),
382
+ _Editor$nodes2 = _slicedToArray(_Editor$nodes, 1),
383
+ link = _Editor$nodes2[0];
384
+ return !!link;
385
+ };
386
+ const unwrapLink = editor => {
387
+ slate.Transforms.unwrapNodes(editor, {
388
+ match: n => !slate.Editor.isEditor(n) && slate.Element.isElement(n) && n.type === 'link'
389
+ });
390
+ };
391
+ const wrapLink = (editor, url) => {
392
+ if (isLinkActive(editor)) {
393
+ unwrapLink(editor);
394
+ }
395
+ const selection = editor.selection;
396
+ const isCollapsed = selection && slate.Range.isCollapsed(selection);
397
+ const linkNode = {
398
+ type: 'link',
399
+ url,
400
+ children: isCollapsed ? [{
401
+ text: url
402
+ }] : []
403
+ };
404
+ if (isCollapsed) {
405
+ slate.Transforms.insertNodes(editor, linkNode);
406
+ } else {
407
+ slate.Transforms.wrapNodes(editor, linkNode, {
408
+ split: true
409
+ });
410
+ slate.Transforms.collapse(editor, {
411
+ edge: 'end'
412
+ });
413
+ }
414
+ };
415
+ const withLinks = editor => {
416
+ const insertText = editor.insertText,
417
+ insertData = editor.insertData,
418
+ isInline = editor.isInline;
419
+
420
+ // Mark link elements as inline (from example)
421
+ editor.isInline = element => {
422
+ return element.type === 'link' || isInline(element);
423
+ };
424
+
425
+ // Handle URL pasting/typing to automatically create links (from example)
426
+ editor.insertText = text => {
427
+ if (text && isUrl__default["default"](text)) {
428
+ wrapLink(editor, text);
429
+ } else {
430
+ insertText(text);
431
+ }
432
+ };
433
+ editor.insertData = data => {
434
+ const text = data.getData('text/plain');
435
+ if (text && isUrl__default["default"](text)) {
436
+ wrapLink(editor, text);
437
+ } else {
438
+ insertData(data);
439
+ }
440
+ };
441
+ return editor;
442
+ };
364
443
 
365
444
  function ownKeys$f(e, r) { var t = _Object$keys__default["default"](e); if (_Object$getOwnPropertySymbols__default["default"]) { var o = _Object$getOwnPropertySymbols__default["default"](e); r && (o = _filterInstanceProperty__default["default"](o).call(o, function (r) { return _Object$getOwnPropertyDescriptor__default["default"](e, r).enumerable; })), t.push.apply(t, o); } return t; }
366
- function _objectSpread$f(e) { for (var r = 1; r < arguments.length; r++) { var _context5, _context6; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty__default["default"](_context5 = ownKeys$f(Object(t), !0)).call(_context5, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](e, _Object$getOwnPropertyDescriptors__default["default"](t)) : _forEachInstanceProperty__default["default"](_context6 = ownKeys$f(Object(t))).call(_context6, function (r) { _Object$defineProperty__default["default"](e, r, _Object$getOwnPropertyDescriptor__default["default"](t, r)); }); } return e; }
445
+ function _objectSpread$f(e) { for (var r = 1; r < arguments.length; r++) { var _context8, _context9; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty__default["default"](_context8 = ownKeys$f(Object(t), !0)).call(_context8, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](e, _Object$getOwnPropertyDescriptors__default["default"](t)) : _forEachInstanceProperty__default["default"](_context9 = ownKeys$f(Object(t))).call(_context9, function (r) { _Object$defineProperty__default["default"](e, r, _Object$getOwnPropertyDescriptor__default["default"](t, r)); }); } return e; }
367
446
 
368
447
  // Slate's way of providing custom type annotations comes down to extending `CustomTypes` interface
369
448
  // more: https://docs.slatejs.org/concepts/12-typescript
@@ -400,15 +479,16 @@ const serializeNode = node => {
400
479
  return string;
401
480
  }
402
481
  const children = _mapInstanceProperty__default["default"](_context = node.children).call(_context, serializeNode).join('');
403
- switch (node.type) {
482
+ const element = node; // Use updated CustomElement
483
+
484
+ switch (element.type) {
404
485
  case 'block-quote':
405
486
  return `<blockquote>${children}</blockquote>`;
406
487
  case 'paragraph':
407
488
  return `<p>${children}</p>`;
408
489
  case 'code':
409
490
  return `<pre>
410
- <code>${children}</code>
411
- </pre>`;
491
+ <code>${children}</code>`;
412
492
  case 'span':
413
493
  return `<span>${children}</span>`;
414
494
  case 'bulleted-list':
@@ -417,6 +497,36 @@ const serializeNode = node => {
417
497
  return `<ol>${children}</ol>`;
418
498
  case 'list-item':
419
499
  return `<li>${children}</li>`;
500
+ case BLOCK_TAGS.a:
501
+ // Handle link serialization
502
+ // eslint-disable-next-line no-case-declarations
503
+ let hrefAttr = '';
504
+ if (element.url) {
505
+ // Sanitize href to prevent javascript: URLs during serialization as well
506
+ const sanitizedUrl = (_context2 => {
507
+ const url = _trimInstanceProperty__default["default"](_context2 = String(element.url)).call(_context2).toLowerCase();
508
+ if (_startsWithInstanceProperty__default["default"](url).call(url, 'javascript:') || _startsWithInstanceProperty__default["default"](url).call(url, 'data:') || _startsWithInstanceProperty__default["default"](url).call(url, 'vbscript:')) {
509
+ return '#';
510
+ }
511
+ return String(element.url);
512
+ })();
513
+ hrefAttr = ` href="${escapeHtml__default["default"](sanitizedUrl)}"`;
514
+ }
515
+ // eslint-disable-next-line no-case-declarations
516
+ let otherAttrsString = '';
517
+ if (element.htmlAttributes && typeof element.htmlAttributes === 'object') {
518
+ for (const _ref of _Object$entries__default["default"](element.htmlAttributes)) {
519
+ var _context3;
520
+ var _ref2 = _slicedToArray(_ref, 2);
521
+ const key = _ref2[0];
522
+ const value = _ref2[1];
523
+ // Strip event handlers during serialization too
524
+ if (!_startsWithInstanceProperty__default["default"](_context3 = key.toLowerCase()).call(_context3, 'on')) {
525
+ otherAttrsString += ` ${escapeHtml__default["default"](key)}="${escapeHtml__default["default"](String(value))}"`;
526
+ }
527
+ }
528
+ }
529
+ return `<a${hrefAttr}${otherAttrsString}>${children}</a>`;
420
530
  case 'heading-one':
421
531
  return `<h1>${children}</h1>`;
422
532
  case 'heading-two':
@@ -424,7 +534,7 @@ const serializeNode = node => {
424
534
  case 'heading-three':
425
535
  return `<h3>${children}</h3>`;
426
536
  case 'heading-four':
427
- return `<h4}>${children}</h4>`;
537
+ return `<h4>${children}</h4>`;
428
538
  case 'heading-five':
429
539
  return `<h5>${children}</h5>`;
430
540
  default:
@@ -446,6 +556,35 @@ const serialize = value => {
446
556
  return outputHtml;
447
557
  };
448
558
  const ELEMENT_TAGS = {
559
+ A: el => {
560
+ const props = {
561
+ type: BLOCK_TAGS.a
562
+ };
563
+ const htmlAttributes = {};
564
+ for (const attr of _Array$from__default["default"](el.attributes)) {
565
+ const attrName = attr.name.toLowerCase();
566
+ const attrValue = attr.value;
567
+ if (attrName === 'href') {
568
+ var _context4;
569
+ // Sanitize href to prevent javascript: URLs
570
+ const sanitizedValue = _trimInstanceProperty__default["default"](_context4 = decodeURI(attrValue)).call(_context4).toLowerCase();
571
+ if (
572
+ // eslint-disable-next-line no-script-url
573
+ _startsWithInstanceProperty__default["default"](sanitizedValue).call(sanitizedValue, 'javascript:') || _startsWithInstanceProperty__default["default"](sanitizedValue).call(sanitizedValue, 'data:') || _startsWithInstanceProperty__default["default"](sanitizedValue).call(sanitizedValue, 'vbscript:')) {
574
+ props.url = '#'; // Replace with a safe value
575
+ } else {
576
+ props.url = attrValue;
577
+ }
578
+ } else if (!_startsWithInstanceProperty__default["default"](attrName).call(attrName, 'on')) {
579
+ // Strip event handlers
580
+ htmlAttributes[attrName] = attrValue;
581
+ }
582
+ }
583
+ if (_Object$keys__default["default"](htmlAttributes).length > 0) {
584
+ props.htmlAttributes = htmlAttributes;
585
+ }
586
+ return props;
587
+ },
449
588
  BLOCKQUOTE: () => ({
450
589
  type: 'quote'
451
590
  }),
@@ -551,7 +690,7 @@ const wrapWithParagraph = textContent => slateHyperscript.jsx('element', {
551
690
  const wrapWithParagraphIfRootElement = (el, textContent) => el.parentNode?.nodeName === 'BODY' // root element, because body is eventually turned to React fragment
552
691
  ? wrapWithParagraph(textContent) : textContent;
553
692
  const deserializeElement = el => {
554
- var _context2, _context3;
693
+ var _context5, _context6;
555
694
  // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType#value
556
695
  if (el.nodeType === 3) {
557
696
  return wrapWithParagraphIfRootElement(el, {
@@ -568,7 +707,7 @@ const deserializeElement = el => {
568
707
  if (nodeName === 'PRE' && el.childNodes[0] && el.childNodes[0].nodeName === 'CODE') {
569
708
  parent = el.childNodes[0];
570
709
  }
571
- let children = _flatInstanceProperty__default["default"](_context2 = _mapInstanceProperty__default["default"](_context3 = _Array$from3__default["default"](parent.childNodes)).call(_context3, deserializeElement)).call(_context2);
710
+ let children = _flatInstanceProperty__default["default"](_context5 = _mapInstanceProperty__default["default"](_context6 = _Array$from__default["default"](parent.childNodes)).call(_context6, deserializeElement)).call(_context5);
572
711
  if (children.length === 0) {
573
712
  children = [{
574
713
  text: ''
@@ -587,11 +726,11 @@ const deserializeElement = el => {
587
726
  type: 'span'
588
727
  }, children));
589
728
  } else {
590
- var _context4;
591
- attrs = _reduceInstanceProperty__default["default"](_context4 = _Object$entries__default["default"](styleObj || {})).call(_context4, (mappedAttrObj, _ref) => {
592
- let _ref2 = _slicedToArray(_ref, 2),
593
- key = _ref2[0],
594
- value = _ref2[1];
729
+ var _context7;
730
+ attrs = _reduceInstanceProperty__default["default"](_context7 = _Object$entries__default["default"](styleObj || {})).call(_context7, (mappedAttrObj, _ref3) => {
731
+ let _ref4 = _slicedToArray(_ref3, 2),
732
+ key = _ref4[0],
733
+ value = _ref4[1];
595
734
  const values = value.split(' '); // to cover the case of space-separated values e.g. `text-decoration-line: "underline line-through"`
596
735
 
597
736
  _forEachInstanceProperty__default["default"](values).call(values, splittedValue => {
@@ -613,8 +752,11 @@ const deserializeElement = el => {
613
752
  _mapInstanceProperty__default["default"](children).call(children, child => slate.Text.isText(child) ? slateHyperscript.jsx('text', attrs, child) : slateHyperscript.jsx('element', attrs, child)));
614
753
  }
615
754
  }
755
+
756
+ // Modified to use the updated ELEMENT_TAGS for 'A'
616
757
  if (ELEMENT_TAGS[nodeName]) {
617
- const attrs = ELEMENT_TAGS[nodeName]();
758
+ const attrs = ELEMENT_TAGS[nodeName](el // Pass element to access its attributes
759
+ );
618
760
  return slateHyperscript.jsx('element', attrs, children);
619
761
  }
620
762
  if (TEXT_TAGS[nodeName]) {
@@ -1731,7 +1873,7 @@ RichTextEditorBody.displayName = 'RichTextEditorBody';
1731
1873
  var RichTextEditorBody$1 = RichTextEditorBody;
1732
1874
 
1733
1875
  // NOTE: This string will be replaced on build time with the package version.
1734
- var version = "19.24.0";
1876
+ var version = "19.25.0";
1735
1877
 
1736
1878
  exports.Element = Element;
1737
1879
  exports.HiddenInput = HiddenInput$1;
@@ -1749,3 +1891,4 @@ exports.toggleBlock = toggleBlock;
1749
1891
  exports.toggleMark = toggleMark;
1750
1892
  exports.validSlateStateAdapter = validSlateStateAdapter;
1751
1893
  exports.version = version;
1894
+ exports.withLinks = withLinks;
@@ -8,9 +8,11 @@ import { warning } from '@commercetools-uikit/utils';
8
8
  import uniq from 'lodash/uniq';
9
9
  import _defineProperty from '@babel/runtime-corejs3/helpers/esm/defineProperty';
10
10
  import _mapInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/map';
11
+ import _trimInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/trim';
12
+ import _startsWithInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/starts-with';
11
13
  import _Array$isArray from '@babel/runtime-corejs3/core-js-stable/array/is-array';
14
+ import _Array$from from '@babel/runtime-corejs3/core-js-stable/array/from';
12
15
  import _flatInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/flat';
13
- import _Array$from3 from '@babel/runtime-corejs3/core-js-stable/array/from';
14
16
  import _forEachInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/for-each';
15
17
  import _Object$getOwnPropertySymbols from '@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols';
16
18
  import _filterInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/filter';
@@ -19,16 +21,17 @@ import _Object$getOwnPropertyDescriptors from '@babel/runtime-corejs3/core-js-st
19
21
  import _Object$defineProperties from '@babel/runtime-corejs3/core-js-stable/object/define-properties';
20
22
  import _Object$defineProperty from '@babel/runtime-corejs3/core-js-stable/object/define-property';
21
23
  import escapeHtml from 'escape-html';
22
- import { Editor, Element as Element$1, Transforms, Text } from 'slate';
24
+ import { Editor, Element as Element$1, Transforms, Text, Range } from 'slate';
23
25
  import { jsx as jsx$1 } from 'slate-hyperscript';
24
26
  import parse from 'style-to-object';
25
27
  import isEmpty$2 from 'lodash/isEmpty';
26
28
  import _includesInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/includes';
29
+ import { useCallback, forwardRef } from 'react';
27
30
  import { ReactEditor, useSlate } from 'slate-react';
31
+ import isUrl from 'is-url';
28
32
  import { jsx, jsxs, Fragment } from '@emotion/react/jsx-runtime';
29
33
  import _someInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/some';
30
34
  import _pt from 'prop-types';
31
- import { useCallback, forwardRef } from 'react';
32
35
  import { accessibleHiddenInputStyles } from '@commercetools-uikit/input-utils';
33
36
  import _objectWithoutProperties from '@babel/runtime-corejs3/helpers/esm/objectWithoutProperties';
34
37
  import _styled from '@emotion/styled/base';
@@ -53,7 +56,8 @@ const BLOCK_TAGS = {
53
56
  pre: 'code',
54
57
  li: 'list-item',
55
58
  ol: 'numbered-list',
56
- ul: 'bulleted-list'
59
+ ul: 'bulleted-list',
60
+ a: 'link'
57
61
  };
58
62
 
59
63
  // Add a dictionary of mark tags.
@@ -137,6 +141,13 @@ const Element = _ref => {
137
141
  }, attributes), {}, {
138
142
  children: children
139
143
  }));
144
+ case BLOCK_TAGS.a:
145
+ return jsx("a", _objectSpread$g(_objectSpread$g({
146
+ style: style
147
+ }, attributes), {}, {
148
+ rel: "noopener noreferrer",
149
+ children: children
150
+ }));
140
151
  default:
141
152
  return jsx("p", _objectSpread$g(_objectSpread$g({
142
153
  style: style
@@ -223,11 +234,11 @@ const toggleMark = (editor, format) => {
223
234
  const isBlockActive = (editor, format) => {
224
235
  const selection = editor.selection;
225
236
  if (!selection) return false;
226
- const _Array$from = _Array$from3(Editor.nodes(editor, {
237
+ const _Array$from$1 = _Array$from(Editor.nodes(editor, {
227
238
  at: Editor.unhangRange(editor, selection),
228
239
  match: n => !Editor.isEditor(n) && Element$1.isElement(n) && n.type === format
229
240
  })),
230
- _Array$from2 = _slicedToArray(_Array$from, 1),
241
+ _Array$from2 = _slicedToArray(_Array$from$1, 1),
231
242
  match = _Array$from2[0];
232
243
  return Boolean(match);
233
244
  };
@@ -326,9 +337,74 @@ const Softbreaker = {
326
337
  return html.split(this.placeholderCharacter).join('');
327
338
  }
328
339
  };
340
+ const isLinkActive = editor => {
341
+ const _Editor$nodes = Editor.nodes(editor, {
342
+ match: n => !Editor.isEditor(n) && Element$1.isElement(n) && n.type === 'link'
343
+ }),
344
+ _Editor$nodes2 = _slicedToArray(_Editor$nodes, 1),
345
+ link = _Editor$nodes2[0];
346
+ return !!link;
347
+ };
348
+ const unwrapLink = editor => {
349
+ Transforms.unwrapNodes(editor, {
350
+ match: n => !Editor.isEditor(n) && Element$1.isElement(n) && n.type === 'link'
351
+ });
352
+ };
353
+ const wrapLink = (editor, url) => {
354
+ if (isLinkActive(editor)) {
355
+ unwrapLink(editor);
356
+ }
357
+ const selection = editor.selection;
358
+ const isCollapsed = selection && Range.isCollapsed(selection);
359
+ const linkNode = {
360
+ type: 'link',
361
+ url,
362
+ children: isCollapsed ? [{
363
+ text: url
364
+ }] : []
365
+ };
366
+ if (isCollapsed) {
367
+ Transforms.insertNodes(editor, linkNode);
368
+ } else {
369
+ Transforms.wrapNodes(editor, linkNode, {
370
+ split: true
371
+ });
372
+ Transforms.collapse(editor, {
373
+ edge: 'end'
374
+ });
375
+ }
376
+ };
377
+ const withLinks = editor => {
378
+ const insertText = editor.insertText,
379
+ insertData = editor.insertData,
380
+ isInline = editor.isInline;
381
+
382
+ // Mark link elements as inline (from example)
383
+ editor.isInline = element => {
384
+ return element.type === 'link' || isInline(element);
385
+ };
386
+
387
+ // Handle URL pasting/typing to automatically create links (from example)
388
+ editor.insertText = text => {
389
+ if (text && isUrl(text)) {
390
+ wrapLink(editor, text);
391
+ } else {
392
+ insertText(text);
393
+ }
394
+ };
395
+ editor.insertData = data => {
396
+ const text = data.getData('text/plain');
397
+ if (text && isUrl(text)) {
398
+ wrapLink(editor, text);
399
+ } else {
400
+ insertData(data);
401
+ }
402
+ };
403
+ return editor;
404
+ };
329
405
 
330
406
  function ownKeys$f(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = _filterInstanceProperty(o).call(o, function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
331
- function _objectSpread$f(e) { for (var r = 1; r < arguments.length; r++) { var _context5, _context6; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context5 = ownKeys$f(Object(t), !0)).call(_context5, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context6 = ownKeys$f(Object(t))).call(_context6, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; }
407
+ function _objectSpread$f(e) { for (var r = 1; r < arguments.length; r++) { var _context8, _context9; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context8 = ownKeys$f(Object(t), !0)).call(_context8, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context9 = ownKeys$f(Object(t))).call(_context9, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; }
332
408
 
333
409
  // Slate's way of providing custom type annotations comes down to extending `CustomTypes` interface
334
410
  // more: https://docs.slatejs.org/concepts/12-typescript
@@ -365,15 +441,16 @@ const serializeNode = node => {
365
441
  return string;
366
442
  }
367
443
  const children = _mapInstanceProperty(_context = node.children).call(_context, serializeNode).join('');
368
- switch (node.type) {
444
+ const element = node; // Use updated CustomElement
445
+
446
+ switch (element.type) {
369
447
  case 'block-quote':
370
448
  return `<blockquote>${children}</blockquote>`;
371
449
  case 'paragraph':
372
450
  return `<p>${children}</p>`;
373
451
  case 'code':
374
452
  return `<pre>
375
- <code>${children}</code>
376
- </pre>`;
453
+ <code>${children}</code>`;
377
454
  case 'span':
378
455
  return `<span>${children}</span>`;
379
456
  case 'bulleted-list':
@@ -382,6 +459,36 @@ const serializeNode = node => {
382
459
  return `<ol>${children}</ol>`;
383
460
  case 'list-item':
384
461
  return `<li>${children}</li>`;
462
+ case BLOCK_TAGS.a:
463
+ // Handle link serialization
464
+ // eslint-disable-next-line no-case-declarations
465
+ let hrefAttr = '';
466
+ if (element.url) {
467
+ // Sanitize href to prevent javascript: URLs during serialization as well
468
+ const sanitizedUrl = (_context2 => {
469
+ const url = _trimInstanceProperty(_context2 = String(element.url)).call(_context2).toLowerCase();
470
+ if (_startsWithInstanceProperty(url).call(url, 'javascript:') || _startsWithInstanceProperty(url).call(url, 'data:') || _startsWithInstanceProperty(url).call(url, 'vbscript:')) {
471
+ return '#';
472
+ }
473
+ return String(element.url);
474
+ })();
475
+ hrefAttr = ` href="${escapeHtml(sanitizedUrl)}"`;
476
+ }
477
+ // eslint-disable-next-line no-case-declarations
478
+ let otherAttrsString = '';
479
+ if (element.htmlAttributes && typeof element.htmlAttributes === 'object') {
480
+ for (const _ref of _Object$entries(element.htmlAttributes)) {
481
+ var _context3;
482
+ var _ref2 = _slicedToArray(_ref, 2);
483
+ const key = _ref2[0];
484
+ const value = _ref2[1];
485
+ // Strip event handlers during serialization too
486
+ if (!_startsWithInstanceProperty(_context3 = key.toLowerCase()).call(_context3, 'on')) {
487
+ otherAttrsString += ` ${escapeHtml(key)}="${escapeHtml(String(value))}"`;
488
+ }
489
+ }
490
+ }
491
+ return `<a${hrefAttr}${otherAttrsString}>${children}</a>`;
385
492
  case 'heading-one':
386
493
  return `<h1>${children}</h1>`;
387
494
  case 'heading-two':
@@ -389,7 +496,7 @@ const serializeNode = node => {
389
496
  case 'heading-three':
390
497
  return `<h3>${children}</h3>`;
391
498
  case 'heading-four':
392
- return `<h4}>${children}</h4>`;
499
+ return `<h4>${children}</h4>`;
393
500
  case 'heading-five':
394
501
  return `<h5>${children}</h5>`;
395
502
  default:
@@ -411,6 +518,35 @@ const serialize = value => {
411
518
  return outputHtml;
412
519
  };
413
520
  const ELEMENT_TAGS = {
521
+ A: el => {
522
+ const props = {
523
+ type: BLOCK_TAGS.a
524
+ };
525
+ const htmlAttributes = {};
526
+ for (const attr of _Array$from(el.attributes)) {
527
+ const attrName = attr.name.toLowerCase();
528
+ const attrValue = attr.value;
529
+ if (attrName === 'href') {
530
+ var _context4;
531
+ // Sanitize href to prevent javascript: URLs
532
+ const sanitizedValue = _trimInstanceProperty(_context4 = decodeURI(attrValue)).call(_context4).toLowerCase();
533
+ if (
534
+ // eslint-disable-next-line no-script-url
535
+ _startsWithInstanceProperty(sanitizedValue).call(sanitizedValue, 'javascript:') || _startsWithInstanceProperty(sanitizedValue).call(sanitizedValue, 'data:') || _startsWithInstanceProperty(sanitizedValue).call(sanitizedValue, 'vbscript:')) {
536
+ props.url = '#'; // Replace with a safe value
537
+ } else {
538
+ props.url = attrValue;
539
+ }
540
+ } else if (!_startsWithInstanceProperty(attrName).call(attrName, 'on')) {
541
+ // Strip event handlers
542
+ htmlAttributes[attrName] = attrValue;
543
+ }
544
+ }
545
+ if (_Object$keys(htmlAttributes).length > 0) {
546
+ props.htmlAttributes = htmlAttributes;
547
+ }
548
+ return props;
549
+ },
414
550
  BLOCKQUOTE: () => ({
415
551
  type: 'quote'
416
552
  }),
@@ -516,7 +652,7 @@ const wrapWithParagraph = textContent => jsx$1('element', {
516
652
  const wrapWithParagraphIfRootElement = (el, textContent) => el.parentNode?.nodeName === 'BODY' // root element, because body is eventually turned to React fragment
517
653
  ? wrapWithParagraph(textContent) : textContent;
518
654
  const deserializeElement = el => {
519
- var _context2, _context3;
655
+ var _context5, _context6;
520
656
  // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType#value
521
657
  if (el.nodeType === 3) {
522
658
  return wrapWithParagraphIfRootElement(el, {
@@ -533,7 +669,7 @@ const deserializeElement = el => {
533
669
  if (nodeName === 'PRE' && el.childNodes[0] && el.childNodes[0].nodeName === 'CODE') {
534
670
  parent = el.childNodes[0];
535
671
  }
536
- let children = _flatInstanceProperty(_context2 = _mapInstanceProperty(_context3 = _Array$from3(parent.childNodes)).call(_context3, deserializeElement)).call(_context2);
672
+ let children = _flatInstanceProperty(_context5 = _mapInstanceProperty(_context6 = _Array$from(parent.childNodes)).call(_context6, deserializeElement)).call(_context5);
537
673
  if (children.length === 0) {
538
674
  children = [{
539
675
  text: ''
@@ -552,11 +688,11 @@ const deserializeElement = el => {
552
688
  type: 'span'
553
689
  }, children));
554
690
  } else {
555
- var _context4;
556
- attrs = _reduceInstanceProperty(_context4 = _Object$entries(styleObj || {})).call(_context4, (mappedAttrObj, _ref) => {
557
- let _ref2 = _slicedToArray(_ref, 2),
558
- key = _ref2[0],
559
- value = _ref2[1];
691
+ var _context7;
692
+ attrs = _reduceInstanceProperty(_context7 = _Object$entries(styleObj || {})).call(_context7, (mappedAttrObj, _ref3) => {
693
+ let _ref4 = _slicedToArray(_ref3, 2),
694
+ key = _ref4[0],
695
+ value = _ref4[1];
560
696
  const values = value.split(' '); // to cover the case of space-separated values e.g. `text-decoration-line: "underline line-through"`
561
697
 
562
698
  _forEachInstanceProperty(values).call(values, splittedValue => {
@@ -578,8 +714,11 @@ const deserializeElement = el => {
578
714
  _mapInstanceProperty(children).call(children, child => Text.isText(child) ? jsx$1('text', attrs, child) : jsx$1('element', attrs, child)));
579
715
  }
580
716
  }
717
+
718
+ // Modified to use the updated ELEMENT_TAGS for 'A'
581
719
  if (ELEMENT_TAGS[nodeName]) {
582
- const attrs = ELEMENT_TAGS[nodeName]();
720
+ const attrs = ELEMENT_TAGS[nodeName](el // Pass element to access its attributes
721
+ );
583
722
  return jsx$1('element', attrs, children);
584
723
  }
585
724
  if (TEXT_TAGS[nodeName]) {
@@ -1802,6 +1941,6 @@ RichTextEditorBody.displayName = 'RichTextEditorBody';
1802
1941
  var RichTextEditorBody$1 = RichTextEditorBody;
1803
1942
 
1804
1943
  // NOTE: This string will be replaced on build time with the package version.
1805
- var version = "19.24.0";
1944
+ var version = "19.25.0";
1806
1945
 
1807
- export { Element, HiddenInput$1 as HiddenInput, Leaf, RichTextEditorBody$1 as RichTextBody, Softbreaker, focusEditor, html$1 as html, isBlockActive, isRichTextEmpty as isEmpty, isMarkActive, index as localized, resetEditor, toggleBlock, toggleMark, validSlateStateAdapter, version };
1946
+ export { Element, HiddenInput$1 as HiddenInput, Leaf, RichTextEditorBody$1 as RichTextBody, Softbreaker, focusEditor, html$1 as html, isBlockActive, isRichTextEmpty as isEmpty, isMarkActive, index as localized, resetEditor, toggleBlock, toggleMark, validSlateStateAdapter, version, withLinks };
@@ -7,6 +7,8 @@ export type CustomElement = {
7
7
  type: Format;
8
8
  children: CustomText[];
9
9
  align?: string;
10
+ url?: string;
11
+ htmlAttributes?: Record<string, string>;
10
12
  };
11
13
  type CustomText = BaseText & {
12
14
  bold?: boolean;
@@ -3,4 +3,4 @@ export { default as html } from "./html/index.js";
3
3
  export { default as isEmpty } from "./is-empty/index.js";
4
4
  export { HiddenInput, RichTextBody } from "./rich-text-body/index.js";
5
5
  export { default as version } from "./version.js";
6
- export { Element, Leaf, Softbreaker, isMarkActive, isBlockActive, toggleMark, toggleBlock, validSlateStateAdapter, resetEditor, focusEditor, } from "./slate-helpers.js";
6
+ export { Element, Leaf, Softbreaker, isMarkActive, isBlockActive, toggleMark, toggleBlock, validSlateStateAdapter, resetEditor, focusEditor, withLinks, } from "./slate-helpers.js";
@@ -37,4 +37,9 @@ declare const Softbreaker: {
37
37
  */
38
38
  cleanHtml(html: string): string;
39
39
  };
40
- export { Element, Leaf, Softbreaker, isMarkActive, isBlockActive, toggleMark, toggleBlock, validSlateStateAdapter, resetEditor, focusEditor, };
40
+ export declare const isLinkActive: (editor: Editor) => boolean;
41
+ export declare const unwrapLink: (editor: Editor) => void;
42
+ export declare const wrapLink: (editor: Editor, url: string) => void;
43
+ export declare const insertLink: (editor: Editor, url: string) => void;
44
+ declare const withLinks: (editor: Editor) => Editor;
45
+ export { Element, Leaf, Softbreaker, isMarkActive, isBlockActive, toggleMark, toggleBlock, validSlateStateAdapter, resetEditor, focusEditor, withLinks, };
@@ -10,6 +10,7 @@ export declare const BLOCK_TAGS: {
10
10
  li: string;
11
11
  ol: string;
12
12
  ul: string;
13
+ a: string;
13
14
  };
14
15
  export declare const MARK_TAGS: {
15
16
  em: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@commercetools-uikit/rich-text-utils",
3
3
  "description": "Utilities for working with rich-text components.",
4
- "version": "19.24.0",
4
+ "version": "19.25.0",
5
5
  "bugs": "https://github.com/commercetools/ui-kit/issues",
6
6
  "repository": {
7
7
  "type": "git",
@@ -24,18 +24,19 @@
24
24
  "dependencies": {
25
25
  "@babel/runtime": "^7.20.13",
26
26
  "@babel/runtime-corejs3": "^7.20.13",
27
- "@commercetools-uikit/design-system": "19.24.0",
28
- "@commercetools-uikit/icons": "19.24.0",
29
- "@commercetools-uikit/input-utils": "19.24.0",
30
- "@commercetools-uikit/spacings-inline": "19.24.0",
31
- "@commercetools-uikit/tooltip": "19.24.0",
32
- "@commercetools-uikit/utils": "19.24.0",
27
+ "@commercetools-uikit/design-system": "19.25.0",
28
+ "@commercetools-uikit/icons": "19.25.0",
29
+ "@commercetools-uikit/input-utils": "19.25.0",
30
+ "@commercetools-uikit/spacings-inline": "19.25.0",
31
+ "@commercetools-uikit/tooltip": "19.25.0",
32
+ "@commercetools-uikit/utils": "19.25.0",
33
33
  "@emotion/react": "^11.10.5",
34
34
  "@emotion/styled": "^11.10.5",
35
35
  "@types/escape-html": "1.0.4",
36
36
  "downshift": "6.1.12",
37
37
  "escape-html": "1.0.3",
38
38
  "is-hotkey": "0.2.0",
39
+ "is-url": "^1.2.4",
39
40
  "lodash": "4.17.21",
40
41
  "prop-types": "15.8.1",
41
42
  "slate": "0.75.0",