@uxf/wysiwyg 11.129.0 → 11.130.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 (148) hide show
  1. package/README.md +9 -7
  2. package/create-all-plugins-with-ui.d.ts +78 -14
  3. package/create-all-plugins-with-ui.js +3 -5
  4. package/create-plugins-with-ui.d.ts +2 -2
  5. package/create-plugins-with-ui.js +25 -30
  6. package/data-format.test.d.ts +1 -0
  7. package/data-format.test.js +132 -0
  8. package/editor-smoke.test.d.ts +1 -0
  9. package/editor-smoke.test.js +9 -0
  10. package/hooks.d.ts +1971 -8
  11. package/hooks.js +18 -11
  12. package/package.json +15 -47
  13. package/plugins/blockquote/create-blockquote-plugin.d.ts +9 -3
  14. package/plugins/blockquote/create-blockquote-plugin.js +31 -3
  15. package/plugins/blockquote/create-blockquote-plugin.test.d.ts +1 -0
  16. package/plugins/blockquote/create-blockquote-plugin.test.js +55 -0
  17. package/plugins/blockquote/types.d.ts +1 -1
  18. package/plugins/blockquote/types.js +2 -2
  19. package/plugins/button/create-button-plugin.d.ts +1 -2
  20. package/plugins/button/create-button-plugin.js +3 -5
  21. package/plugins/button/transforms/is-button-active.js +1 -2
  22. package/plugins/button/transforms/update-button.js +1 -2
  23. package/plugins/constants.test.d.ts +1 -0
  24. package/plugins/constants.test.js +52 -0
  25. package/plugins/embedded/types.d.ts +6 -0
  26. package/plugins/embedded/types.js +2 -0
  27. package/plugins/embedded/utils.d.ts +2 -1
  28. package/plugins/embedded/utils.js +4 -4
  29. package/plugins/embedded/video/create-video-plugin.d.ts +10 -3
  30. package/plugins/embedded/video/create-video-plugin.js +8 -9
  31. package/plugins/embedded/video/create-video-plugin.test.d.ts +1 -0
  32. package/plugins/embedded/video/create-video-plugin.test.js +30 -0
  33. package/plugins/embedded/video/update-video.js +1 -2
  34. package/plugins/exit-break/create-exit-break-plugin.d.ts +8 -3
  35. package/plugins/exit-break/create-exit-break-plugin.js +8 -25
  36. package/plugins/heading/create-headings-plugin.d.ts +13 -4
  37. package/plugins/heading/create-headings-plugin.js +22 -36
  38. package/plugins/heading/create-headings-plugin.test.d.ts +1 -0
  39. package/plugins/heading/create-headings-plugin.test.js +42 -0
  40. package/plugins/highlight/createHighlightPlugin.d.ts +4 -2
  41. package/plugins/highlight/createHighlightPlugin.js +8 -21
  42. package/plugins/highlight/types.d.ts +1 -2
  43. package/plugins/image/create-image-plugin.d.ts +3 -3
  44. package/plugins/image/create-image-plugin.js +58 -91
  45. package/plugins/image/create-image-plugin.test.d.ts +1 -0
  46. package/plugins/image/create-image-plugin.test.js +33 -0
  47. package/plugins/image/transforms/is-image-active.js +1 -2
  48. package/plugins/image/transforms/update-image.js +1 -2
  49. package/plugins/image/types.d.ts +6 -3
  50. package/plugins/image/types.js +2 -2
  51. package/plugins/image/utils/is-image-url.js +2 -2
  52. package/plugins/image/with-paste-image-url.d.ts +5 -5
  53. package/plugins/image/with-paste-image-url.js +29 -25
  54. package/plugins/image/with-paste-image.d.ts +4 -7
  55. package/plugins/image/with-paste-image.js +41 -39
  56. package/plugins/link/constants.d.ts +1 -1
  57. package/plugins/link/constants.js +2 -2
  58. package/plugins/link/create-link-plugin.d.ts +38 -3
  59. package/plugins/link/create-link-plugin.js +9 -7
  60. package/plugins/link/create-link-plugin.test.d.ts +1 -0
  61. package/plugins/link/create-link-plugin.test.js +24 -0
  62. package/plugins/link/floating-link-wrapper.d.ts +14 -7
  63. package/plugins/link/floating-link-wrapper.js +46 -17
  64. package/plugins/link/hooks/index.d.ts +1 -1
  65. package/plugins/link/hooks/index.js +2 -2
  66. package/plugins/link/hooks/use-floating-link-selectors.d.ts +8 -0
  67. package/plugins/link/hooks/use-floating-link-selectors.js +21 -0
  68. package/plugins/link/hooks/use-link-actions.d.ts +991 -8
  69. package/plugins/link/hooks/use-link-actions.js +22 -19
  70. package/plugins/link/hooks/use-link-cancel-on-escape.d.ts +1 -1
  71. package/plugins/link/hooks/use-link-cancel-on-escape.js +2 -2
  72. package/plugins/link/hooks/use-link-submit-on-enter.js +5 -5
  73. package/plugins/link/transforms/get-active-link.d.ts +1 -1
  74. package/plugins/link/transforms/get-active-link.js +3 -5
  75. package/plugins/link/transforms/insert-link.js +9 -9
  76. package/plugins/link/transforms/submit-link.d.ts +2 -3
  77. package/plugins/link/transforms/submit-link.js +13 -13
  78. package/plugins/link/transforms/trigger-link-insert-or-edit.js +2 -2
  79. package/plugins/link/transforms/unwrap-link.js +2 -2
  80. package/plugins/list/constants.d.ts +11 -3
  81. package/plugins/list/constants.js +14 -10
  82. package/plugins/list/create-list-plugin.d.ts +26 -4
  83. package/plugins/list/create-list-plugin.js +13 -12
  84. package/plugins/list/create-list-plugin.test.d.ts +1 -0
  85. package/plugins/list/create-list-plugin.test.js +20 -0
  86. package/plugins/mark-bold/create-bold-plugin.d.ts +3 -3
  87. package/plugins/mark-bold/create-bold-plugin.js +2 -2
  88. package/plugins/mark-bold/types.d.ts +2 -2
  89. package/plugins/mark-bold/types.js +3 -3
  90. package/plugins/mark-code/create-code-plugin.d.ts +7 -3
  91. package/plugins/mark-code/create-code-plugin.js +6 -2
  92. package/plugins/mark-code/types.d.ts +2 -2
  93. package/plugins/mark-code/types.js +3 -3
  94. package/plugins/mark-italic/create-italic-plugin.d.ts +3 -3
  95. package/plugins/mark-italic/create-italic-plugin.js +2 -2
  96. package/plugins/mark-italic/types.d.ts +2 -2
  97. package/plugins/mark-italic/types.js +3 -3
  98. package/plugins/mark-underline/create-underline-plugin.d.ts +3 -3
  99. package/plugins/mark-underline/create-underline-plugin.js +2 -2
  100. package/plugins/mark-underline/types.d.ts +2 -2
  101. package/plugins/mark-underline/types.js +3 -3
  102. package/plugins/marks.test.d.ts +1 -0
  103. package/plugins/marks.test.js +42 -0
  104. package/plugins/node-id/add-ids-to-nodes.d.ts +1 -1
  105. package/plugins/node-id/create-node-id-plugin.d.ts +22 -3
  106. package/plugins/node-id/create-node-id-plugin.js +3 -2
  107. package/plugins/paragraph/create-paragraph-plugin.d.ts +1 -3
  108. package/plugins/paragraph/create-paragraph-plugin.js +3 -4
  109. package/plugins/paragraph/types.d.ts +1 -1
  110. package/plugins/paragraph/types.js +2 -2
  111. package/plugins/structural.test.d.ts +1 -0
  112. package/plugins/structural.test.js +29 -0
  113. package/plugins/trailing-block/create-trailing-block-plugin.d.ts +13 -3
  114. package/plugins/trailing-block/create-trailing-block-plugin.js +8 -2
  115. package/serializers/serialize-to-plaintext.js +2 -2
  116. package/styles/styles.css +1 -0
  117. package/types.d.ts +23 -14
  118. package/ui/floating-link.js +28 -28
  119. package/ui/highlight-mark.d.ts +2 -3
  120. package/ui/highlight-mark.js +4 -1
  121. package/ui/link-element.js +6 -3
  122. package/ui/toolbar/buttons/button-toolbar-button.d.ts +1 -1
  123. package/ui/toolbar/buttons/element-toolbar-button.d.ts +1 -1
  124. package/ui/toolbar/buttons/image-toolbar-button.d.ts +1 -1
  125. package/ui/toolbar/buttons/link-toolbar-button.d.ts +1 -1
  126. package/ui/toolbar/buttons/link-toolbar-button.js +1 -1
  127. package/ui/toolbar/buttons/list-toolbar-button.d.ts +1 -1
  128. package/ui/toolbar/buttons/list-toolbar-button.js +3 -3
  129. package/ui/toolbar/buttons/mark-toolbar-button.d.ts +1 -1
  130. package/ui/toolbar/buttons/undo-redo-button-group.d.ts +1 -1
  131. package/ui/toolbar/buttons/video-toolbar-button.d.ts +1 -1
  132. package/ui/toolbar/types.d.ts +1 -1
  133. package/ui/utils.js +1 -1
  134. package/utils.d.ts +18 -19
  135. package/utils.js +31 -28
  136. package/utils.test.d.ts +1 -0
  137. package/utils.test.js +23 -0
  138. package/wysiwyg-editor.d.ts +2 -2
  139. package/wysiwyg-editor.js +29 -23
  140. package/wysiwyg-editor.test.js +111 -6
  141. package/plugins/image/with-image.d.ts +0 -7
  142. package/plugins/image/with-image.js +0 -19
  143. package/plugins/reset-node/create-reset-node.d.ts +0 -3
  144. package/plugins/reset-node/create-reset-node.js +0 -28
  145. package/plugins/select-on-backspace/create-select-on-backspace-plugin.d.ts +0 -3
  146. package/plugins/select-on-backspace/create-select-on-backspace-plugin.js +0 -14
  147. package/plugins/soft-break/create-soft-break-plugin.d.ts +0 -3
  148. package/plugins/soft-break/create-soft-break-plugin.js +0 -22
package/utils.js CHANGED
@@ -1,36 +1,39 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.removeSelectedNode = exports.insertVoid = exports.removeElement = exports.getActiveElement = exports.getSelectedNode = exports.isSomeOfPluginsEnabled = exports.isPluginEnabled = exports.toggleMark = exports.isMarkActive = exports.isRangeInSingleText = exports.focusEditor = exports.toggleNodeType = exports.someNode = exports.getPluginType = exports.getPluginOptions = exports.getUxfEditor = void 0;
4
- const plate_break_1 = require("@udecode/plate-break");
5
- const plate_common_1 = require("@udecode/plate-common");
6
- const plate_core_1 = require("@udecode/plate-core");
7
- const slate_1 = require("@udecode/slate");
8
- const slate_react_1 = require("@udecode/slate-react");
9
4
  const is_not_nil_1 = require("@uxf/core/utils/is-not-nil");
10
5
  const types_1 = require("./plugins/paragraph/types");
11
- const getUxfEditor = (editor) => (0, slate_1.getTEditor)(editor);
6
+ const getUxfEditor = (editor) => editor;
12
7
  exports.getUxfEditor = getUxfEditor;
13
- const getPluginOptions = (editor, pluginKey) => {
14
- return (0, plate_core_1.getPluginOptions)(editor, pluginKey);
15
- };
8
+ const getPluginOptions = (editor, pluginKey) => editor.getOptions({ key: pluginKey });
16
9
  exports.getPluginOptions = getPluginOptions;
17
- const getPluginType = (editor, key) => (0, plate_core_1.getPluginType)(editor, key);
10
+ const getPluginType = (editor, key) => editor.getType(key);
18
11
  exports.getPluginType = getPluginType;
19
- const someNode = (editor, options) => (0, plate_common_1.someNode)(editor, options);
12
+ const someNode = (editor, options) => editor.api.some(options);
20
13
  exports.someNode = someNode;
21
- const toggleNodeType = (editor, options, editorNodesOptions) => (0, plate_core_1.toggleNodeType)(editor, options, editorNodesOptions);
14
+ const toggleNodeType = (editor, options, editorNodesOptions) => {
15
+ var _a;
16
+ return editor.tf.toggleBlock((_a = options.activeType) !== null && _a !== void 0 ? _a : types_1.PARAGRAPH_PLUGIN_TYPE, {
17
+ defaultType: options.inactiveType,
18
+ someOptions: editorNodesOptions,
19
+ });
20
+ };
22
21
  exports.toggleNodeType = toggleNodeType;
23
- const focusEditor = (editor, target) => (0, plate_common_1.focusEditor)(editor, target);
22
+ const focusEditor = (editor, target) => editor.tf.focus({ at: target });
24
23
  exports.focusEditor = focusEditor;
25
- const isRangeInSingleText = (editor) => editor.selection ? (0, plate_common_1.isRangeInSingleText)(editor.selection) : false;
24
+ const isRangeInSingleText = (editor) => (editor.selection ? editor.api.isAt({ text: true }) : false);
26
25
  exports.isRangeInSingleText = isRangeInSingleText;
27
- const isMarkActive = (editor, type) => (0, plate_common_1.isMarkActive)(editor, type) && (0, exports.isRangeInSingleText)(editor);
26
+ const isMarkActive = (editor, type) => Boolean(editor.api.mark(type)) && (0, exports.isRangeInSingleText)(editor);
28
27
  exports.isMarkActive = isMarkActive;
29
- const toggleMark = (editor, { key, clear }) => (0, plate_common_1.toggleMark)(editor, { key, clear });
28
+ const toggleMark = (editor, { key, clear }) => editor.tf.toggleMark(key, { remove: clear });
30
29
  exports.toggleMark = toggleMark;
31
- const isPluginEnabled = (editor, pluginKey) => editor.plugins.findIndex((plugin) => plugin.key === pluginKey) !== -1;
30
+ /**
31
+ * `editor.plugins` is a keyed record in Plate 53, not the array it was in Plate 20. Both helpers gate
32
+ * every toolbar button, so indexing it as an array here would hide the whole toolbar.
33
+ */
34
+ const isPluginEnabled = (editor, pluginKey) => Boolean(editor.plugins[pluginKey]);
32
35
  exports.isPluginEnabled = isPluginEnabled;
33
- const isSomeOfPluginsEnabled = (editor, pluginKeys) => editor.plugins.some((plugin) => pluginKeys.includes(plugin.key));
36
+ const isSomeOfPluginsEnabled = (editor, pluginKeys) => pluginKeys.some((pluginKey) => (0, exports.isPluginEnabled)(editor, pluginKey));
34
37
  exports.isSomeOfPluginsEnabled = isSomeOfPluginsEnabled;
35
38
  const getSelectedNode = (editor) => {
36
39
  var _a;
@@ -44,11 +47,11 @@ const getSelectedNode = (editor) => {
44
47
  exports.getSelectedNode = getSelectedNode;
45
48
  const getActiveElement = (editor, elementKey) => {
46
49
  var _a;
47
- const entry = (0, slate_1.findNode)(editor, {
50
+ const entry = editor.api.node({
51
+ at: (_a = editor.selection) !== null && _a !== void 0 ? _a : undefined,
48
52
  match: {
49
53
  type: (0, exports.getPluginType)(editor, elementKey),
50
54
  },
51
- at: (_a = editor.selection) !== null && _a !== void 0 ? _a : undefined,
52
55
  });
53
56
  if (!entry) {
54
57
  return;
@@ -59,8 +62,8 @@ const getActiveElement = (editor, elementKey) => {
59
62
  exports.getActiveElement = getActiveElement;
60
63
  const removeElement = (editor, node) => {
61
64
  var _a;
62
- const path = (0, slate_react_1.findNodePath)(editor, node);
63
- (0, slate_1.removeNodes)(editor, { at: path });
65
+ const path = editor.api.findPath(node);
66
+ editor.tf.removeNodes({ at: path });
64
67
  (0, exports.focusEditor)(editor, (_a = editor.selection) !== null && _a !== void 0 ? _a : undefined);
65
68
  return true;
66
69
  };
@@ -69,20 +72,20 @@ const insertVoid = (editor, node) => {
69
72
  var _a;
70
73
  const selectedNode = (0, exports.getSelectedNode)(editor);
71
74
  if (!selectedNode) {
72
- (0, slate_1.insertNodes)(editor, node);
75
+ editor.tf.insertNodes(node);
73
76
  }
74
77
  else {
75
- if (selectedNode.type !== types_1.PARAGRAPH_PLUGIN_TYPE || !(0, slate_1.isElementEmpty)(editor, selectedNode)) {
76
- (0, plate_break_1.exitBreak)(editor, {});
78
+ if (selectedNode.type !== types_1.PARAGRAPH_PLUGIN_TYPE || !editor.api.isEmpty(selectedNode)) {
79
+ editor.tf.insertExitBreak();
77
80
  }
78
- (0, slate_1.setNodes)(editor, node);
81
+ editor.tf.setNodes(node);
79
82
  }
80
83
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
81
- (0, exports.focusEditor)(editor, (_a = editor.selection) !== null && _a !== void 0 ? _a : editor.prevSelection);
84
+ (0, exports.focusEditor)(editor, (_a = editor.selection) !== null && _a !== void 0 ? _a : editor.dom.prevSelection);
82
85
  };
83
86
  exports.insertVoid = insertVoid;
84
87
  const removeSelectedNode = (editor) => {
85
- (0, slate_1.removeNodes)(editor);
88
+ editor.tf.removeNodes();
86
89
  (0, exports.focusEditor)(editor);
87
90
  };
88
91
  exports.removeSelectedNode = removeSelectedNode;
@@ -0,0 +1 @@
1
+ export {};
package/utils.test.js ADDED
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const platejs_1 = require("platejs");
4
+ const create_paragraph_plugin_1 = require("./plugins/paragraph/create-paragraph-plugin");
5
+ const utils_1 = require("./utils");
6
+ function editor() {
7
+ return (0, platejs_1.createSlateEditor)({ plugins: [(0, create_paragraph_plugin_1.createParagraphPlugin)()] });
8
+ }
9
+ describe("plugin lookup helpers", () => {
10
+ it("resolves a plugin key to its stored node type", () => {
11
+ expect((0, utils_1.getPluginType)(editor(), "p")).toBe("paragraph");
12
+ });
13
+ it("reports a registered plugin as enabled", () => {
14
+ expect((0, utils_1.isPluginEnabled)(editor(), "p")).toBe(true);
15
+ });
16
+ it("reports an unregistered plugin as disabled", () => {
17
+ expect((0, utils_1.isPluginEnabled)(editor(), "image")).toBe(false);
18
+ });
19
+ it("matches when any one of several keys is registered", () => {
20
+ expect((0, utils_1.isSomeOfPluginsEnabled)(editor(), ["image", "p"])).toBe(true);
21
+ expect((0, utils_1.isSomeOfPluginsEnabled)(editor(), ["image", "video"])).toBe(false);
22
+ });
23
+ });
@@ -1,5 +1,5 @@
1
- import { TEditableProps } from "@udecode/plate-core";
2
1
  import { Noop } from "@uxf/core/types";
2
+ import { PlateContentProps } from "platejs/react";
3
3
  import { FC, ForwardedRef, ReactNode } from "react";
4
4
  import { UxfPlatePlugin, WysiwygContent } from "./types";
5
5
  export interface WysiwygEditorHandle {
@@ -8,7 +8,7 @@ export interface WysiwygEditorHandle {
8
8
  export interface WysiwygEditorProps {
9
9
  className?: string;
10
10
  customPluginsToolbarButtons?: ReactNode;
11
- editableProps?: TEditableProps<WysiwygContent>;
11
+ editableProps?: PlateContentProps;
12
12
  editorRef?: ForwardedRef<WysiwygEditorHandle>;
13
13
  id: string;
14
14
  initialValue: WysiwygContent | undefined;
package/wysiwyg-editor.js CHANGED
@@ -35,13 +35,12 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  })();
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
37
  exports.WysiwygEditor = void 0;
38
- const plate_core_1 = require("@udecode/plate-core");
39
38
  const classes_1 = require("@uxf/core/constants/classes");
40
39
  const use_is_mounted_1 = require("@uxf/core-react/hooks/use-is-mounted");
41
40
  const translations_1 = require("@uxf/core-react/translations");
42
41
  const loader_1 = require("@uxf/ui/loader");
43
- const react_1 = __importStar(require("react"));
44
- const slate_1 = require("slate");
42
+ const react_1 = require("platejs/react");
43
+ const react_2 = __importStar(require("react"));
45
44
  const hooks_1 = require("./hooks");
46
45
  const toolbar_1 = require("./ui/toolbar/toolbar");
47
46
  const utils_1 = require("./utils");
@@ -50,13 +49,12 @@ const CLASS_NAME = "uxf-wysiwyg";
50
49
  * Component that accesses the editor from Slate context and exposes focus method via ref
51
50
  * Must be rendered inside Plate component
52
51
  */
53
- const EditorRefHandler = (0, react_1.forwardRef)((props, ref) => {
52
+ const EditorRefHandler = (0, react_2.forwardRef)((props, ref) => {
54
53
  const editor = (0, hooks_1.useUxfEditorRef)();
55
- (0, react_1.useImperativeHandle)(ref, () => ({
54
+ (0, react_2.useImperativeHandle)(ref, () => ({
56
55
  focus: () => {
57
56
  // Select the end of the document first
58
- const end = slate_1.Editor.end(editor, []);
59
- slate_1.Transforms.select(editor, end);
57
+ editor.tf.select(editor.api.end([]));
60
58
  // Then focus the editor
61
59
  (0, utils_1.focusEditor)(editor);
62
60
  },
@@ -67,27 +65,35 @@ EditorRefHandler.displayName = "EditorRefHandler";
67
65
  const Editor = (props) => {
68
66
  var _a, _b;
69
67
  const t = (0, translations_1.useUxfTranslation)();
70
- return (react_1.default.createElement(react_1.default.Fragment, null,
71
- react_1.default.createElement(toolbar_1.Toolbar, { customPluginsButtons: props.customPluginsToolbarButtons, leftElement: props.toolbarLeftElement, plateId: props.plateId, rightElement: props.toolbarRightElement }),
72
- react_1.default.createElement("div", { className: `${CLASS_NAME}__content` },
73
- react_1.default.createElement(plate_core_1.Plate, { editableProps: {
74
- autoFocus: false,
75
- placeholder: t("uxf-wysiwyg:common.placeholder"),
76
- readOnly: false,
77
- spellCheck: false,
78
- ...props.editableProps,
79
- className: `${CLASS_NAME}__editable ${(_b = (_a = props.editableProps) === null || _a === void 0 ? void 0 : _a.className) !== null && _b !== void 0 ? _b : ""}`,
80
- }, id: props.plateId, normalizeInitialValue: true }, props.editorRef && react_1.default.createElement(EditorRefHandler, { ref: props.editorRef })))));
68
+ /**
69
+ * `<Plate>` renders no DOM of its own, so the toolbar moving inside it leaves the markup identical
70
+ * while letting it take the editor from context instead of by id.
71
+ */
72
+ const editor = (0, react_1.usePlateEditor)({
73
+ id: props.id,
74
+ plugins: props.plugins,
75
+ shouldNormalizeEditor: true,
76
+ value: props.initialValue,
77
+ }, [props.plugins]);
78
+ return (react_2.default.createElement(react_1.Plate, { editor: editor, onChange: ({ value }) => props.onChange(value) },
79
+ react_2.default.createElement(toolbar_1.Toolbar, { customPluginsButtons: props.customPluginsToolbarButtons, leftElement: props.toolbarLeftElement, plateId: props.id, rightElement: props.toolbarRightElement }),
80
+ react_2.default.createElement("div", { className: `${CLASS_NAME}__content` },
81
+ react_2.default.createElement(react_1.PlateContent, { autoFocus: false,
82
+ placeholder: t("uxf-wysiwyg:common.placeholder"),
83
+ readOnly: false,
84
+ spellCheck: false,
85
+ ...props.editableProps,
86
+ className: `${CLASS_NAME}__editable ${(_b = (_a = props.editableProps) === null || _a === void 0 ? void 0 : _a.className) !== null && _b !== void 0 ? _b : ""}` }),
87
+ props.editorRef && react_2.default.createElement(EditorRefHandler, { ref: props.editorRef }))));
81
88
  };
82
89
  const WysiwygEditor = (props) => {
83
90
  var _a, _b;
84
91
  const isMounted = (0, use_is_mounted_1.useIsMounted)();
85
92
  if (!isMounted) {
86
- return (react_1.default.createElement("div", { className: `${CLASS_NAME} ${CLASS_NAME}--${classes_1.CLASSES.IS_LOADING} ${(_a = props.className) !== null && _a !== void 0 ? _a : ""}` },
87
- react_1.default.createElement(loader_1.Loader, { className: `${CLASS_NAME}__loader` })));
93
+ return (react_2.default.createElement("div", { className: `${CLASS_NAME} ${CLASS_NAME}--${classes_1.CLASSES.IS_LOADING} ${(_a = props.className) !== null && _a !== void 0 ? _a : ""}` },
94
+ react_2.default.createElement(loader_1.Loader, { className: `${CLASS_NAME}__loader` })));
88
95
  }
89
- return (react_1.default.createElement("div", { className: `${CLASS_NAME} ${(_b = props.className) !== null && _b !== void 0 ? _b : ""}` },
90
- react_1.default.createElement(plate_core_1.PlateProvider, { id: props.id, initialValue: props.initialValue, onChange: props.onChange, plugins: props.plugins },
91
- react_1.default.createElement(Editor, { customPluginsToolbarButtons: props.customPluginsToolbarButtons, editableProps: props.editableProps, editorRef: props.editorRef, plateId: props.id, toolbarLeftElement: props.toolbarLeftElement, toolbarRightElement: props.toolbarRightElement }))));
96
+ return (react_2.default.createElement("div", { className: `${CLASS_NAME} ${(_b = props.className) !== null && _b !== void 0 ? _b : ""}` },
97
+ react_2.default.createElement(Editor, { customPluginsToolbarButtons: props.customPluginsToolbarButtons, editableProps: props.editableProps, editorRef: props.editorRef, id: props.id, initialValue: props.initialValue, onChange: props.onChange, plugins: props.plugins, toolbarLeftElement: props.toolbarLeftElement, toolbarRightElement: props.toolbarRightElement })));
92
98
  };
93
99
  exports.WysiwygEditor = WysiwygEditor;
@@ -58,19 +58,124 @@ const UI_CONTEXT = {
58
58
  translationFn: (key) => key,
59
59
  };
60
60
  const INITIAL_VALUE = [{ type: "paragraph", children: [{ text: "hello" }] }];
61
- function renderEditor(editorRef) {
62
- return (0, react_1.render)(react_2.default.createElement(context_1.UiContextProvider, { value: UI_CONTEXT },
61
+ // Plate's post-mount normalisation writes to its jotai store, which notifies subscribers a microtask
62
+ // later, outside render's act scope. Flushing it here settles the editor before the assertions.
63
+ async function settle() {
64
+ await (0, react_1.act)(async () => undefined);
65
+ }
66
+ async function renderEditor(editorRef) {
67
+ const result = (0, react_1.render)(react_2.default.createElement(context_1.UiContextProvider, { value: UI_CONTEXT },
63
68
  react_2.default.createElement(wysiwyg_editor_1.WysiwygEditor, { editorRef: editorRef, id: "test-editor", initialValue: INITIAL_VALUE, onChange: () => undefined, plugins: (0, create_plugins_with_ui_1.createPluginsWithUi)([(0, create_all_plugins_with_ui_1.createBoldPluginWithUi)()]) })));
69
+ await settle();
70
+ return result;
71
+ }
72
+ const FILE = { extension: ".png", id: 1, name: "a.png", namespace: "wysiwyg", uuid: "u" };
73
+ /** One node of every element type, so each plugin's component has something to render. */
74
+ const EVERY_ELEMENT = [
75
+ { children: [{ text: "Nadpis" }], type: "h1" },
76
+ {
77
+ children: [
78
+ { bold: true, text: "tučně" },
79
+ { italic: true, text: "kurzíva" },
80
+ { text: "podtržené", underline: true },
81
+ { code: true, text: "kód" },
82
+ { highlight: true, text: "zvýrazněné" },
83
+ ],
84
+ type: "paragraph",
85
+ },
86
+ { children: [{ text: "citace" }], type: "blockquote" },
87
+ { children: [{ children: [{ children: [{ text: "položka" }], type: "lic" }], type: "li" }], type: "ul" },
88
+ { children: [{ children: [{ children: [{ text: "první" }], type: "lic" }], type: "li" }], type: "ol" },
89
+ {
90
+ children: [
91
+ { text: "" },
92
+ { children: [{ text: "odkaz" }], target: "_blank", type: "link", url: "https://uxf.cz" },
93
+ { text: "" },
94
+ ],
95
+ type: "paragraph",
96
+ },
97
+ { alt: "alt", children: [{ text: "" }], file: FILE, type: "image", url: "https://example.test/a.png" },
98
+ {
99
+ children: [{ text: "" }],
100
+ embeddedUrl: "https://www.youtube.com/embed/x",
101
+ metaData: null,
102
+ provider: "youtube",
103
+ type: "video",
104
+ url: "https://youtu.be/x",
105
+ videoId: "x",
106
+ },
107
+ { buttonText: "klik", buttonUrl: "https://uxf.cz", children: [{ text: "" }], type: "button" },
108
+ ];
109
+ async function renderEditorWithAllPlugins() {
110
+ const result = (0, react_1.render)(react_2.default.createElement(context_1.UiContextProvider, { value: UI_CONTEXT },
111
+ react_2.default.createElement(wysiwyg_editor_1.WysiwygEditor, { id: "all-plugins-editor", initialValue: EVERY_ELEMENT, onChange: () => undefined, plugins: (0, create_all_plugins_with_ui_1.createAllPluginsWithUi)({
112
+ image: { getImageUrl: (file) => file.name, uploadImage: async () => FILE },
113
+ }) })));
114
+ await settle();
115
+ return result;
64
116
  }
117
+ // Act warnings fail the test instead of scrolling past.
118
+ let actWarnings = [];
119
+ let consoleError;
120
+ beforeEach(() => {
121
+ actWarnings = [];
122
+ consoleError = jest.spyOn(console, "error").mockImplementation((...args) => {
123
+ if (String(args.at(0)).includes("not wrapped in act")) {
124
+ actWarnings.push(String(args.at(0)));
125
+ }
126
+ });
127
+ });
128
+ afterEach(() => {
129
+ consoleError.mockRestore();
130
+ expect(actWarnings).toEqual([]);
131
+ });
65
132
  describe("WysiwygEditor", () => {
66
- it("renders without an editorRef", () => {
67
- const { container } = renderEditor();
133
+ it("renders without an editorRef", async () => {
134
+ const { container } = await renderEditor();
68
135
  expect(container.querySelector(".uxf-wysiwyg__editable")).not.toBeNull();
69
136
  });
70
- it("exposes focus() through editorRef", () => {
137
+ it("exposes focus() through editorRef", async () => {
71
138
  var _a;
72
139
  const editorRef = (0, react_2.createRef)();
73
- renderEditor(editorRef);
140
+ await renderEditor(editorRef);
74
141
  expect(typeof ((_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.focus)).toBe("function");
75
142
  });
143
+ // Catches a plugin whose component never got attached: it typechecks, the editor renders, and the
144
+ // node silently comes out as a bare div.
145
+ it.each([
146
+ ["uxf-wysiwyg__element-heading-h1"],
147
+ ["uxf-wysiwyg__element-blockquote"],
148
+ ["uxf-wysiwyg__element-list-unordered"],
149
+ ["uxf-wysiwyg__element-list-ordered"],
150
+ ["uxf-wysiwyg__element-list-item"],
151
+ ["uxf-wysiwyg__element-link"],
152
+ ["uxf-wysiwyg__element-image"],
153
+ ["uxf-wysiwyg__element-video"],
154
+ ["uxf-wysiwyg__element-button"],
155
+ ])("renders %s through its own component", async (className) => {
156
+ const { container } = await renderEditorWithAllPlugins();
157
+ expect(container.querySelector(`.${className}`)).not.toBeNull();
158
+ });
159
+ // The element tests above pass even when a mark component is broken, which is how the highlight
160
+ // regression got through: the <mark> rendered, it just never received its colour.
161
+ it.each([
162
+ ["uxf-wysiwyg__element-bold"],
163
+ ["uxf-wysiwyg__element-italic"],
164
+ ["uxf-wysiwyg__element-underline"],
165
+ ["uxf-wysiwyg__element-code"],
166
+ ["uxf-wysiwyg__element-highlight"],
167
+ ])("renders %s through its own component", async (className) => {
168
+ const { container } = await renderEditorWithAllPlugins();
169
+ expect(container.querySelector(`.${className}`)).not.toBeNull();
170
+ });
171
+ it("gives highlight its configured colour, not a stray DOM attribute", async () => {
172
+ const { container } = await renderEditorWithAllPlugins();
173
+ const mark = container.querySelector(".uxf-wysiwyg__element-highlight");
174
+ expect(mark === null || mark === void 0 ? void 0 : mark.style.getPropertyValue("--highlight-color")).toBe("#fde047");
175
+ expect(mark === null || mark === void 0 ? void 0 : mark.hasAttribute("color")).toBe(false);
176
+ });
177
+ it("renders the paragraph component, which the facade attaches itself", async () => {
178
+ const { container } = await renderEditorWithAllPlugins();
179
+ expect(container.querySelector(".uxf-wysiwyg__element-paragraph")).not.toBeNull();
180
+ });
76
181
  });
@@ -1,7 +0,0 @@
1
- import { WithPlatePlugin } from "@udecode/plate-core";
2
- import { UxfEditor, WysiwygContent } from "../../types";
3
- import { UxfImagePluginOptions } from "./types";
4
- /**
5
- * @see withPasteImageUrl
6
- */
7
- export declare const withImage: (editor: UxfEditor, plugin: WithPlatePlugin<UxfImagePluginOptions, WysiwygContent, UxfEditor>) => UxfEditor;
@@ -1,19 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.withImage = void 0;
4
- const with_paste_image_1 = require("./with-paste-image");
5
- const with_paste_image_url_1 = require("./with-paste-image-url");
6
- /**
7
- * @see withPasteImageUrl
8
- */
9
- const withImage = (editor, plugin) => {
10
- const { options: { disableUploadOnPasteImage, disableUploadOnPasteImageUrl }, } = plugin;
11
- if (!disableUploadOnPasteImage) {
12
- editor = (0, with_paste_image_1.withPasteImage)(editor, plugin);
13
- }
14
- if (!disableUploadOnPasteImageUrl) {
15
- editor = (0, with_paste_image_url_1.withPasteImageUrl)(editor, plugin);
16
- }
17
- return editor;
18
- };
19
- exports.withImage = withImage;
@@ -1,3 +0,0 @@
1
- import { ResetNodePlugin } from "@udecode/plate-reset-node";
2
- import { UxfPlatePlugin } from "../../types";
3
- export declare const createResetNodePlugin: () => UxfPlatePlugin<ResetNodePlugin>;
@@ -1,28 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createResetNodePlugin = void 0;
4
- const plate_common_1 = require("@udecode/plate-common");
5
- const plate_reset_node_1 = require("@udecode/plate-reset-node");
6
- const types_1 = require("../blockquote/types");
7
- const types_2 = require("../paragraph/types");
8
- const resetBlockTypesCommonRule = {
9
- types: [types_1.BLOCKQUOTE_PLUGIN_KEY],
10
- defaultType: types_2.PARAGRAPH_PLUGIN_KEY,
11
- };
12
- const createResetNodePlugin = () => (0, plate_reset_node_1.createResetNodePlugin)({
13
- options: {
14
- rules: [
15
- {
16
- ...resetBlockTypesCommonRule,
17
- hotkey: "Enter",
18
- predicate: plate_common_1.isBlockAboveEmpty,
19
- },
20
- {
21
- ...resetBlockTypesCommonRule,
22
- hotkey: "Backspace",
23
- predicate: plate_common_1.isSelectionAtBlockStart,
24
- },
25
- ],
26
- },
27
- });
28
- exports.createResetNodePlugin = createResetNodePlugin;
@@ -1,3 +0,0 @@
1
- import { SelectOnBackspacePlugin } from "@udecode/plate-select";
2
- import { UxfPlatePlugin } from "../../types";
3
- export declare const createSelectOnBackspacePlugin: () => UxfPlatePlugin<SelectOnBackspacePlugin>;
@@ -1,14 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createSelectOnBackspacePlugin = void 0;
4
- const plate_media_1 = require("@udecode/plate-media");
5
- const plate_select_1 = require("@udecode/plate-select");
6
- const types_1 = require("../image/types");
7
- const createSelectOnBackspacePlugin = () => (0, plate_select_1.createSelectOnBackspacePlugin)({
8
- options: {
9
- query: {
10
- allow: [types_1.IMAGE_PLUGIN_KEY, plate_media_1.ELEMENT_MEDIA_EMBED],
11
- },
12
- },
13
- });
14
- exports.createSelectOnBackspacePlugin = createSelectOnBackspacePlugin;
@@ -1,3 +0,0 @@
1
- import { SoftBreakPlugin } from "@udecode/plate-break";
2
- import { UxfEditor, WysiwygContent } from "../../types";
3
- export declare const createSoftBreakPlugin: () => import("@udecode/plate-core").PlatePlugin<SoftBreakPlugin, WysiwygContent, UxfEditor>;
@@ -1,22 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createSoftBreakPlugin = void 0;
4
- const plate_break_1 = require("@udecode/plate-break");
5
- const types_1 = require("../blockquote/types");
6
- const types_2 = require("../paragraph/types");
7
- const createSoftBreakPlugin = () => (0, plate_break_1.createSoftBreakPlugin)({
8
- options: {
9
- rules: [
10
- {
11
- hotkey: "shift+enter",
12
- },
13
- {
14
- hotkey: "enter",
15
- query: {
16
- allow: [types_2.PARAGRAPH_PLUGIN_KEY, types_1.BLOCKQUOTE_PLUGIN_KEY],
17
- },
18
- },
19
- ],
20
- },
21
- });
22
- exports.createSoftBreakPlugin = createSoftBreakPlugin;