@uxf/wysiwyg 11.125.0 → 11.127.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.
package/README.md CHANGED
@@ -80,10 +80,7 @@ To enable only some plugins, compose them yourself with `createPluginsWithUi`:
80
80
  ```tsx
81
81
  import { createBoldPluginWithUi, createHeadingsPluginWithUi, createPluginsWithUi } from "@uxf/wysiwyg";
82
82
 
83
- const plugins = createPluginsWithUi([
84
- createHeadingsPluginWithUi({ disabledLevels: [1] }),
85
- createBoldPluginWithUi(),
86
- ]);
83
+ const plugins = createPluginsWithUi([createHeadingsPluginWithUi({ disabledLevels: [1] }), createBoldPluginWithUi()]);
87
84
  ```
88
85
 
89
86
  ## Content model
@@ -113,55 +110,56 @@ Everything below is exported from the package root (`@uxf/wysiwyg`) unless a dee
113
110
 
114
111
  `import { WysiwygEditor } from "@uxf/wysiwyg";`
115
112
 
116
- | Prop | Type | Required | Description |
117
- | --- | --- | --- | --- |
118
- | `id` | `string` | yes | Unique Plate instance id. Must be unique per editor on the page. |
119
- | `initialValue` | `WysiwygContent \| undefined` | yes | Document the editor is seeded with on mount. |
120
- | `onChange` | `(value: WysiwygContent) => void` | yes | Called on every edit with the full document. |
121
- | `plugins` | `UxfPlatePlugin[]` | yes | Build with `createAllPluginsWithUi` / `createPluginsWithUi`. |
122
- | `className` | `string` | no | Class on the editor root wrapper. |
123
- | `editableProps` | `TEditableProps<WysiwygContent>` | no | Passed to Plate's editable area (`placeholder`, `readOnly`, `autoFocus`, `spellCheck`, `className`, …). Defaults: `autoFocus: false`, `readOnly: false`, `spellCheck: false`, localized placeholder. |
124
- | `customPluginsToolbarButtons` | `ReactNode` | no | Extra toolbar buttons appended to the built-in ones. |
125
- | `toolbarLeftElement` | `ReactNode` | no | Node rendered at the toolbar's left edge. |
126
- | `toolbarRightElement` | `ReactNode` | no | Node rendered at the toolbar's right edge. |
113
+ | Prop | Type | Required | Description |
114
+ | ----------------------------- | ----------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
115
+ | `id` | `string` | yes | Unique Plate instance id. Must be unique per editor on the page. |
116
+ | `initialValue` | `WysiwygContent \| undefined` | yes | Document the editor is seeded with on mount. |
117
+ | `onChange` | `(value: WysiwygContent) => void` | yes | Called on every edit with the full document. |
118
+ | `plugins` | `UxfPlatePlugin[]` | yes | Build with `createAllPluginsWithUi` / `createPluginsWithUi`. |
119
+ | `className` | `string` | no | Class on the editor root wrapper. |
120
+ | `editableProps` | `TEditableProps<WysiwygContent>` | no | Passed to Plate's editable area (`placeholder`, `readOnly`, `autoFocus`, `spellCheck`, `className`, …). Defaults: `autoFocus: false`, `readOnly: false`, `spellCheck: false`, localized placeholder. |
121
+ | `editorRef` | `ForwardedRef<WysiwygEditorHandle>` | no | Imperative handle exposing `focus()`, which selects the end of the document and then focuses the editable area. |
122
+ | `customPluginsToolbarButtons` | `ReactNode` | no | Extra toolbar buttons appended to the built-in ones. |
123
+ | `toolbarLeftElement` | `ReactNode` | no | Node rendered at the toolbar's left edge. |
124
+ | `toolbarRightElement` | `ReactNode` | no | Node rendered at the toolbar's right edge. |
127
125
 
128
126
  ### Plugin builders
129
127
 
130
128
  `createPluginsWithUi` / `createAllPluginsWithUi` return the `UxfPlatePlugin[]` you pass to `WysiwygEditor`. Both inject the base plugins automatically (paragraph, exit-break, reset-node, soft-break, node-id, select-on-backspace, trailing-block), so pass only the feature builders below — not raw Plate plugins.
131
129
 
132
- | Export | Signature | Notes |
133
- | --- | --- | --- |
134
- | `createPluginsWithUi` | `(plugins: Array<WysiwygPlugin \| WysiwygRecursivePlugin<string>>, options?: { overrideByKey?: MyOverrideByKey }) => UxfPlatePlugin[]` | Compose a custom subset. |
135
- | `createAllPluginsWithUi` | `(options: CreateAllPluginsOptions) => UxfPlatePlugin[]` | Enable every plugin. |
136
- | `createHeadingsPluginWithUi` | `(options?: HeadingsPluginOptions) => WysiwygRecursivePlugin` | `disabledLevels?: (1..6)[]`. |
137
- | `createBoldPluginWithUi` | `() => WysiwygPlugin` | |
138
- | `createItalicPluginWithUi` | `() => WysiwygPlugin` | |
139
- | `createUnderlinePluginWithUi` | `() => WysiwygPlugin` | |
140
- | `createCodePluginWithUi` | `() => WysiwygPlugin` | |
141
- | `createHighlightPluginWithUi` | `(color?: CSSProperties["color"]) => WysiwygPlugin` | Defaults to `twColors.yellow[300]`. |
142
- | `createBlockquotePluginWithUi` | `() => WysiwygPlugin` | |
143
- | `createListPluginWithUi` | `() => WysiwygRecursivePlugin` | Renders `ul` / `ol` / `li`. |
144
- | `createLinkPluginWithUi` | `() => WysiwygPlugin` | |
145
- | `createImagePluginWithUi` | `(options: UxfImagePluginOptions) => WysiwygPlugin` | See options below. |
146
- | `createVideoPluginWithUi` | `() => WysiwygPlugin` | |
147
- | `createButtonPluginWithUi` | `() => WysiwygPlugin` | |
130
+ | Export | Signature | Notes |
131
+ | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
132
+ | `createPluginsWithUi` | `(plugins: Array<WysiwygPlugin \| WysiwygRecursivePlugin<string>>, options?: { overrideByKey?: MyOverrideByKey }) => UxfPlatePlugin[]` | Compose a custom subset. |
133
+ | `createAllPluginsWithUi` | `(options: CreateAllPluginsOptions) => UxfPlatePlugin[]` | Enable every plugin. |
134
+ | `createHeadingsPluginWithUi` | `(options?: HeadingsPluginOptions) => WysiwygRecursivePlugin` | `disabledLevels?: (1..6)[]`. |
135
+ | `createBoldPluginWithUi` | `() => WysiwygPlugin` | |
136
+ | `createItalicPluginWithUi` | `() => WysiwygPlugin` | |
137
+ | `createUnderlinePluginWithUi` | `() => WysiwygPlugin` | |
138
+ | `createCodePluginWithUi` | `() => WysiwygPlugin` | |
139
+ | `createHighlightPluginWithUi` | `(color?: CSSProperties["color"]) => WysiwygPlugin` | Defaults to `twColors.yellow[300]`. |
140
+ | `createBlockquotePluginWithUi` | `() => WysiwygPlugin` | |
141
+ | `createListPluginWithUi` | `() => WysiwygRecursivePlugin` | Renders `ul` / `ol` / `li`. |
142
+ | `createLinkPluginWithUi` | `() => WysiwygPlugin` | |
143
+ | `createImagePluginWithUi` | `(options: UxfImagePluginOptions) => WysiwygPlugin` | See options below. |
144
+ | `createVideoPluginWithUi` | `() => WysiwygPlugin` | |
145
+ | `createButtonPluginWithUi` | `() => WysiwygPlugin` | |
148
146
 
149
147
  `CreateAllPluginsOptions`:
150
148
 
151
- | Key | Type | Required | Description |
152
- | --- | --- | --- | --- |
153
- | `image` | `UxfImagePluginOptions` | yes | Image plugin options (pass `{}` to enable without upload handlers). |
154
- | `headings` | `HeadingsPluginOptions` | no | e.g. `{ disabledLevels: [1] }`. |
155
- | `highlightColor` | `CSSProperties["color"]` | no | Highlight mark color. |
149
+ | Key | Type | Required | Description |
150
+ | ---------------- | ------------------------ | -------- | ------------------------------------------------------------------- |
151
+ | `image` | `UxfImagePluginOptions` | yes | Image plugin options (pass `{}` to enable without upload handlers). |
152
+ | `headings` | `HeadingsPluginOptions` | no | e.g. `{ disabledLevels: [1] }`. |
153
+ | `highlightColor` | `CSSProperties["color"]` | no | Highlight mark color. |
156
154
 
157
155
  `UxfImagePluginOptions` (extends Plate's `MediaPlugin`):
158
156
 
159
- | Key | Type | Description |
160
- | --- | --- | --- |
161
- | `uploadImage` | `(file: File) => Promise<FileResponse>` | Upload handler; returns a `FileResponse` (`@uxf/core/types`). |
162
- | `getImageUrl` | `(file: FileResponse) => string` | Resolves the display URL for an uploaded file. |
163
- | `disableUploadOnPasteImageUrl` | `boolean` | Disable auto-upload when an image URL is pasted. |
164
- | `disableUploadOnPasteImage` | `boolean` | Disable auto-upload when an image blob is pasted. |
157
+ | Key | Type | Description |
158
+ | ------------------------------ | --------------------------------------- | ------------------------------------------------------------- |
159
+ | `uploadImage` | `(file: File) => Promise<FileResponse>` | Upload handler; returns a `FileResponse` (`@uxf/core/types`). |
160
+ | `getImageUrl` | `(file: FileResponse) => string` | Resolves the display URL for an uploaded file. |
161
+ | `disableUploadOnPasteImageUrl` | `boolean` | Disable auto-upload when an image URL is pasted. |
162
+ | `disableUploadOnPasteImage` | `boolean` | Disable auto-upload when an image blob is pasted. |
165
163
 
166
164
  ### Hooks
167
165
 
@@ -183,6 +181,21 @@ serializeToPlaintext(value); // keepIndentation defaults to true (joins blocks w
183
181
 
184
182
  ### Types
185
183
 
184
+ `WysiwygEditorHandle` — the shape behind `editorRef`:
185
+
186
+ ```tsx
187
+ import { WysiwygEditor } from "@uxf/wysiwyg";
188
+ // deep import: this type is not re-exported from the package index
189
+ import { WysiwygEditorHandle } from "@uxf/wysiwyg/wysiwyg-editor";
190
+ import { useRef } from "react";
191
+
192
+ const editorRef = useRef<WysiwygEditorHandle>(null);
193
+
194
+ <WysiwygEditor editorRef={editorRef} id="body" initialValue={value} onChange={setValue} plugins={plugins} />;
195
+
196
+ editorRef.current?.focus(); // caret goes to the end of the document
197
+ ```
198
+
186
199
  The full node model and editor types are re-exported, including: `WysiwygContent`, `WysiwygRootBlock`, `RichText`, the element interfaces (`UxfParagraphElement`, `UxfHeadingElement`, `UxfBlockQuoteElement`, `UxfLinkElement`, `UxfImageElement`, `UxfVideoElement`, `UxfButtonElement`, `UxfUnorderedListElement`, `UxfOrderedListElement`, `LiElement`, `LicElement`), plus `UxfEditor`, `UxfPlatePlugin`, `WysiwygPlugin`, `WysiwygRecursivePlugin`, and the render prop/component types (`RenderElementProps`, `ElementUiComponent`, `RenderLeafProps`, `LeafUiComponent`, `RenderAfterEditable`, `MyOverrideByKey`, `UiComponents`).
187
200
 
188
201
  ## Gotchas
@@ -192,6 +205,7 @@ The full node model and editor types are re-exported, including: `WysiwygContent
192
205
  - **Build plugins with the provided factories.** Pass only the results of `createPluginsWithUi` / `createAllPluginsWithUi` to `plugins`; they inject the required base plugins. Do not hand-assemble raw Plate plugin arrays.
193
206
  - **CSS is required and Tailwind-dependent.** Without `@uxf/wysiwyg/styles/styles.css` the editor is unstyled; the file relies on your Tailwind/PostCSS setup and `@uxf/ui`'s `uxf-typo-*` utilities.
194
207
  - **Translations are required.** Register `@uxf/wysiwyg/translations/translations`, otherwise labels/tooltips render as raw keys.
208
+ - **`WysiwygEditorHandle` is not on the package index.** `index.ts` exports only the `WysiwygEditor` component, so the ref type has to come from the deep import `@uxf/wysiwyg/wysiwyg-editor` — which is what `@uxf/cms`'s own `WysiwygInput` does.
195
209
  - **Pinned peers.** Plate/Slate and `@uxf/*` peer versions are pinned exactly (see `package.json`); mismatched versions will not work.
196
210
 
197
211
  ## Links
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uxf/wysiwyg",
3
- "version": "11.125.0",
3
+ "version": "11.127.0",
4
4
  "description": "UXF Wysiwyg editor",
5
5
  "author": "Robin Dvorak <dvorak@uxf.cz>",
6
6
  "homepage": "https://gitlab.com/uxf-npm/wysiwyg",
@@ -31,9 +31,9 @@
31
31
  "@udecode/slate": "19.8.0",
32
32
  "@udecode/slate-react": "19.7.1",
33
33
  "@udecode/slate-utils": "19.7.1",
34
- "@uxf/core": "11.124.0",
35
- "@uxf/core-react": "11.125.0",
36
- "@uxf/ui": "11.125.0",
34
+ "@uxf/core": "11.127.0",
35
+ "@uxf/core-react": "11.127.0",
36
+ "@uxf/ui": "11.127.0",
37
37
  "react": ">=18.2.0",
38
38
  "react-dom": ">=18.2.0",
39
39
  "slate": "0.90.0",
@@ -62,9 +62,9 @@
62
62
  "@udecode/slate": "19.8.0",
63
63
  "@udecode/slate-react": "19.7.1",
64
64
  "@udecode/slate-utils": "19.7.1",
65
- "@uxf/core": "11.124.0",
66
- "@uxf/core-react": "11.125.0",
67
- "@uxf/ui": "11.125.0",
65
+ "@uxf/core": "11.127.0",
66
+ "@uxf/core-react": "11.127.0",
67
+ "@uxf/ui": "11.127.0",
68
68
  "react": "18.3.1",
69
69
  "react-dom": "18.3.1",
70
70
  "slate": "0.90.0",
@@ -1,10 +1,15 @@
1
1
  import { TEditableProps } from "@udecode/plate-core";
2
- import { FC, ReactNode } from "react";
2
+ import { Noop } from "@uxf/core/types";
3
+ import { FC, ForwardedRef, ReactNode } from "react";
3
4
  import { UxfPlatePlugin, WysiwygContent } from "./types";
5
+ export interface WysiwygEditorHandle {
6
+ focus: Noop;
7
+ }
4
8
  export interface WysiwygEditorProps {
5
9
  className?: string;
6
10
  customPluginsToolbarButtons?: ReactNode;
7
11
  editableProps?: TEditableProps<WysiwygContent>;
12
+ editorRef?: ForwardedRef<WysiwygEditorHandle>;
8
13
  id: string;
9
14
  initialValue: WysiwygContent | undefined;
10
15
  onChange: (value: WysiwygContent) => void;
package/wysiwyg-editor.js CHANGED
@@ -1,8 +1,38 @@
1
1
  "use strict";
2
2
  "use client";
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod };
5
- };
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
6
36
  Object.defineProperty(exports, "__esModule", { value: true });
7
37
  exports.WysiwygEditor = void 0;
8
38
  const plate_core_1 = require("@udecode/plate-core");
@@ -10,9 +40,30 @@ const classes_1 = require("@uxf/core/constants/classes");
10
40
  const use_is_mounted_1 = require("@uxf/core-react/hooks/use-is-mounted");
11
41
  const translations_1 = require("@uxf/core-react/translations");
12
42
  const loader_1 = require("@uxf/ui/loader");
13
- const react_1 = __importDefault(require("react"));
43
+ const react_1 = __importStar(require("react"));
44
+ const slate_1 = require("slate");
45
+ const hooks_1 = require("./hooks");
14
46
  const toolbar_1 = require("./ui/toolbar/toolbar");
47
+ const utils_1 = require("./utils");
15
48
  const CLASS_NAME = "uxf-wysiwyg";
49
+ /**
50
+ * Component that accesses the editor from Slate context and exposes focus method via ref
51
+ * Must be rendered inside Plate component
52
+ */
53
+ const EditorRefHandler = (0, react_1.forwardRef)((props, ref) => {
54
+ const editor = (0, hooks_1.useUxfEditorRef)();
55
+ (0, react_1.useImperativeHandle)(ref, () => ({
56
+ focus: () => {
57
+ // Select the end of the document first
58
+ const end = slate_1.Editor.end(editor, []);
59
+ slate_1.Transforms.select(editor, end);
60
+ // Then focus the editor
61
+ (0, utils_1.focusEditor)(editor);
62
+ },
63
+ }));
64
+ return null;
65
+ });
66
+ EditorRefHandler.displayName = "EditorRefHandler";
16
67
  const Editor = (props) => {
17
68
  var _a, _b;
18
69
  const t = (0, translations_1.useUxfTranslation)();
@@ -26,7 +77,8 @@ const Editor = (props) => {
26
77
  spellCheck: false,
27
78
  ...props.editableProps,
28
79
  className: `${CLASS_NAME}__editable ${(_b = (_a = props.editableProps) === null || _a === void 0 ? void 0 : _a.className) !== null && _b !== void 0 ? _b : ""}`,
29
- }, id: props.plateId, normalizeInitialValue: true }))));
80
+ }, id: props.plateId, normalizeInitialValue: true }),
81
+ props.editorRef && react_1.default.createElement(EditorRefHandler, { ref: props.editorRef }))));
30
82
  };
31
83
  const WysiwygEditor = (props) => {
32
84
  var _a, _b;
@@ -37,6 +89,6 @@ const WysiwygEditor = (props) => {
37
89
  }
38
90
  return (react_1.default.createElement("div", { className: `${CLASS_NAME} ${(_b = props.className) !== null && _b !== void 0 ? _b : ""}` },
39
91
  react_1.default.createElement(plate_core_1.PlateProvider, { id: props.id, initialValue: props.initialValue, onChange: props.onChange, plugins: props.plugins },
40
- react_1.default.createElement(Editor, { customPluginsToolbarButtons: props.customPluginsToolbarButtons, editableProps: props.editableProps, plateId: props.id, toolbarLeftElement: props.toolbarLeftElement, toolbarRightElement: props.toolbarRightElement }))));
92
+ react_1.default.createElement(Editor, { customPluginsToolbarButtons: props.customPluginsToolbarButtons, editableProps: props.editableProps, editorRef: props.editorRef, plateId: props.id, toolbarLeftElement: props.toolbarLeftElement, toolbarRightElement: props.toolbarRightElement }))));
41
93
  };
42
94
  exports.WysiwygEditor = WysiwygEditor;