@mdxeditor/editor 3.40.1 → 3.41.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
@@ -26,7 +26,7 @@ If you find a bug, check if something similar is not reported already in the [is
26
26
 
27
27
  If you're integrating the component in your commercial project and need dedicated assistance with your issues in exchange of sponsorship, [contact me over email](mailto:petyo@mdxeditor.dev).
28
28
 
29
- If you want to discuss ideas [join the Discord server](https://discord.gg/4q7U2Hc) or start a discussion in the [Discussions](https://github.com/mdx-editor/editor/discussions) section.
29
+ If you want to discuss ideas start a discussion in the [Discussions](https://github.com/mdx-editor/editor/discussions) section.
30
30
 
31
31
  ## License
32
32
 
package/dist/index.d.ts CHANGED
@@ -268,6 +268,12 @@ export declare const ALL_HEADING_LEVELS: readonly [1, 2, 3, 4, 5, 6];
268
268
  */
269
269
  export declare const allowedHeadingLevels$: NodeRef<readonly HEADING_LEVEL[]>;
270
270
 
271
+ /**
272
+ * Allow to set width and height of image through dialog window
273
+ * @group Image
274
+ */
275
+ export declare const allowSetImageDimensions$: NodeRef<boolean>;
276
+
271
277
  /**
272
278
  * returns a function which when called always returns the passed value
273
279
  * @group Utils
@@ -311,6 +317,8 @@ export declare const applyListType$: NodeRef<"" | ListType>;
311
317
  declare interface BaseImageParameters {
312
318
  altText?: string;
313
319
  title?: string;
320
+ width?: number;
321
+ height?: number;
314
322
  }
315
323
 
316
324
  /**
@@ -1086,6 +1094,8 @@ declare interface EditImageToolbarProps {
1086
1094
  initialImagePath: string | null;
1087
1095
  title: string;
1088
1096
  alt: string;
1097
+ width?: number | 'inherit';
1098
+ height?: number | 'inherit';
1089
1099
  }
1090
1100
 
1091
1101
  /**
@@ -1416,6 +1426,7 @@ export declare const imagePlugin: (params?: {
1416
1426
  imageAutocompleteSuggestions?: string[] | undefined;
1417
1427
  disableImageResize?: boolean | undefined;
1418
1428
  disableImageSettingsButton?: boolean | undefined;
1429
+ allowSetImageDimensions?: boolean | undefined;
1419
1430
  imagePreviewHandler?: ImagePreviewHandler | undefined;
1420
1431
  ImageDialog?: FC<{}> | (() => JSX.Element) | undefined;
1421
1432
  EditImageToolbar?: FC<{}> | (() => JSX.Element) | undefined;
@@ -2441,6 +2452,8 @@ export declare const openLinkEditDialog$: NodeRef<void>;
2441
2452
  */
2442
2453
  export declare const openNewImageDialog$: NodeRef<void>;
2443
2454
 
2455
+ export declare const parseImageDimension: (value: string | number | undefined) => number | undefined;
2456
+
2444
2457
  /* Excluded from this release type: placeholder$ */
2445
2458
 
2446
2459
  /**
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import { insertThematicBreak$, thematicBreakPlugin } from "./plugins/thematic-br
10
10
  import { applyListType$, currentListType$, listsPlugin } from "./plugins/lists/index.js";
11
11
  import { insertTable$, tablePlugin } from "./plugins/table/index.js";
12
12
  import { disableAutoLink$, linkPlugin } from "./plugins/link/index.js";
13
- import { INSERT_IMAGE_COMMAND, closeImageDialog$, disableImageResize$, disableImageSettingsButton$, editImageToolbarComponent$, imageAutocompleteSuggestions$, imageDialogState$, imagePlaceholder$, imagePlugin, imagePreviewHandler$, imageUploadHandler$, insertImage$, openEditImageDialog$, openNewImageDialog$, saveImage$ } from "./plugins/image/index.js";
13
+ import { INSERT_IMAGE_COMMAND, allowSetImageDimensions$, closeImageDialog$, disableImageResize$, disableImageSettingsButton$, editImageToolbarComponent$, imageAutocompleteSuggestions$, imageDialogState$, imagePlaceholder$, imagePlugin, imagePreviewHandler$, imageUploadHandler$, insertImage$, openEditImageDialog$, openNewImageDialog$, parseImageDimension, saveImage$ } from "./plugins/image/index.js";
14
14
  import { frontmatterDialogOpen$, frontmatterPlugin, hasFrontmatter$, insertFrontmatter$, removeFrontmatter$ } from "./plugins/frontmatter/index.js";
15
15
  import { quotePlugin } from "./plugins/quote/index.js";
16
16
  import { maxLengthPlugin } from "./plugins/maxlength/index.js";
@@ -180,6 +180,7 @@ export {
180
180
  addToMarkdownExtension$,
181
181
  addTopAreaChild$,
182
182
  admonitionLabelsMap,
183
+ allowSetImageDimensions$,
183
184
  allowedHeadingLevels$,
184
185
  always,
185
186
  appendCodeBlockEditorDescriptor$,
@@ -304,6 +305,7 @@ export {
304
305
  openEditImageDialog$,
305
306
  openLinkEditDialog$,
306
307
  openNewImageDialog$,
308
+ parseImageDimension,
307
309
  placeholder$,
308
310
  prop,
309
311
  quotePlugin,
@@ -3,10 +3,11 @@ import { useCellValues, usePublisher } from "@mdxeditor/gurx";
3
3
  import classNames from "classnames";
4
4
  import { $getNodeByKey } from "lexical";
5
5
  import React__default from "react";
6
- import { disableImageSettingsButton$, openEditImageDialog$ } from "./index.js";
6
+ import { disableImageSettingsButton$, openEditImageDialog$, parseImageDimension } from "./index.js";
7
7
  import styles from "../../styles/ui.module.css.js";
8
8
  import { iconComponentFor$, readOnly$, useTranslation } from "../core/index.js";
9
- function EditImageToolbar({ nodeKey, imageSource, initialImagePath, title, alt }) {
9
+ function EditImageToolbar(props) {
10
+ const { nodeKey, imageSource, initialImagePath, title, alt, width, height } = props;
10
11
  const [disableImageSettingsButton, iconComponentFor, readOnly] = useCellValues(disableImageSettingsButton$, iconComponentFor$, readOnly$);
11
12
  const [editor] = useLexicalComposerContext();
12
13
  const openEditImageDialog = usePublisher(openEditImageDialog$);
@@ -40,7 +41,9 @@ function EditImageToolbar({ nodeKey, imageSource, initialImagePath, title, alt }
40
41
  initialValues: {
41
42
  src: !initialImagePath ? imageSource : initialImagePath,
42
43
  title,
43
- altText: alt
44
+ altText: alt,
45
+ width: parseImageDimension(width),
46
+ height: parseImageDimension(height)
44
47
  }
45
48
  });
46
49
  }
@@ -4,15 +4,16 @@ import React__default from "react";
4
4
  import { useForm } from "react-hook-form";
5
5
  import styles from "../../styles/ui.module.css.js";
6
6
  import { editorRootElementRef$, useTranslation } from "../core/index.js";
7
- import { imageAutocompleteSuggestions$, imageDialogState$, imageUploadHandler$, saveImage$, closeImageDialog$ } from "./index.js";
7
+ import { imageAutocompleteSuggestions$, imageDialogState$, imageUploadHandler$, allowSetImageDimensions$, saveImage$, closeImageDialog$ } from "./index.js";
8
8
  import { DownshiftAutoComplete } from "../core/ui/DownshiftAutoComplete.js";
9
9
  import { useCellValues, usePublisher } from "@mdxeditor/gurx";
10
10
  const ImageDialog = () => {
11
- const [imageAutocompleteSuggestions, state, editorRootElementRef, imageUploadHandler] = useCellValues(
11
+ const [imageAutocompleteSuggestions, state, editorRootElementRef, imageUploadHandler, allowSetImageDimensions] = useCellValues(
12
12
  imageAutocompleteSuggestions$,
13
13
  imageDialogState$,
14
14
  editorRootElementRef$,
15
- imageUploadHandler$
15
+ imageUploadHandler$,
16
+ allowSetImageDimensions$
16
17
  );
17
18
  const saveImage = usePublisher(saveImage$);
18
19
  const closeImageDialog = usePublisher(closeImageDialog$);
@@ -21,6 +22,9 @@ const ImageDialog = () => {
21
22
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
22
23
  values: state.type === "editing" ? state.initialValues : {}
23
24
  });
25
+ const resetFormState = () => {
26
+ reset({ src: "", title: "", altText: "", width: void 0, height: void 0 });
27
+ };
24
28
  if (state.type === "inactive")
25
29
  return null;
26
30
  return /* @__PURE__ */ React__default.createElement(
@@ -30,7 +34,7 @@ const ImageDialog = () => {
30
34
  onOpenChange: (open) => {
31
35
  if (!open) {
32
36
  closeImageDialog();
33
- reset({ src: "", title: "", altText: "" });
37
+ resetFormState();
34
38
  }
35
39
  }
36
40
  },
@@ -50,7 +54,7 @@ const ImageDialog = () => {
50
54
  e.preventDefault();
51
55
  e.stopPropagation();
52
56
  await handleSubmit(saveImage)(e);
53
- reset({ src: "", title: "", altText: "" });
57
+ resetFormState();
54
58
  },
55
59
  className: styles.multiFieldForm
56
60
  },
@@ -69,6 +73,7 @@ const ImageDialog = () => {
69
73
  )),
70
74
  /* @__PURE__ */ React__default.createElement("div", { className: styles.formField }, /* @__PURE__ */ React__default.createElement("label", { htmlFor: "alt" }, t("uploadImage.alt", "Alt:")), /* @__PURE__ */ React__default.createElement("input", { type: "text", ...register("altText"), className: styles.textInput })),
71
75
  /* @__PURE__ */ React__default.createElement("div", { className: styles.formField }, /* @__PURE__ */ React__default.createElement("label", { htmlFor: "title" }, t("uploadImage.title", "Title:")), /* @__PURE__ */ React__default.createElement("input", { type: "text", ...register("title"), className: styles.textInput })),
76
+ allowSetImageDimensions && /* @__PURE__ */ React__default.createElement("div", { className: styles.imageDimensionsContainer }, /* @__PURE__ */ React__default.createElement("div", { className: styles.formField }, /* @__PURE__ */ React__default.createElement("label", { htmlFor: "width" }, t("uploadImage.width", "Width:")), /* @__PURE__ */ React__default.createElement("input", { type: "number", min: 0, ...register("width"), className: styles.textInput })), /* @__PURE__ */ React__default.createElement("div", { className: styles.formField }, /* @__PURE__ */ React__default.createElement("label", { htmlFor: "height" }, t("uploadImage.height", "Height:")), /* @__PURE__ */ React__default.createElement("input", { type: "number", min: 0, ...register("height"), className: styles.textInput }))),
72
77
  /* @__PURE__ */ React__default.createElement("div", { style: { display: "flex", justifyContent: "flex-end", gap: "var(--spacing-2)" } }, /* @__PURE__ */ React__default.createElement(
73
78
  "button",
74
79
  {
@@ -5,7 +5,7 @@ import { mergeRegister } from "@lexical/utils";
5
5
  import { useCellValues } from "@mdxeditor/gurx";
6
6
  import classNames from "classnames";
7
7
  import { $isNodeSelection, $getSelection, $getNodeByKey, $setSelection, SELECTION_CHANGE_COMMAND, COMMAND_PRIORITY_LOW, CLICK_COMMAND, DRAGSTART_COMMAND, KEY_DELETE_COMMAND, KEY_BACKSPACE_COMMAND, KEY_ENTER_COMMAND, KEY_ESCAPE_COMMAND } from "lexical";
8
- import { imagePlaceholder$, disableImageResize$, imagePreviewHandler$, editImageToolbarComponent$ } from "./index.js";
8
+ import { imagePlaceholder$, disableImageResize$, allowSetImageDimensions$, imagePreviewHandler$, editImageToolbarComponent$ } from "./index.js";
9
9
  import styles from "../../styles/ui.module.css.js";
10
10
  import { readOnly$ } from "../core/index.js";
11
11
  import { $isImageNode } from "./ImageNode.js";
@@ -66,9 +66,10 @@ function LazyImage({
66
66
  );
67
67
  }
68
68
  function ImageEditor({ src, title, alt, nodeKey, width, height, rest }) {
69
- const [ImagePlaceholderComponent, disableImageResize, imagePreviewHandler, readOnly, EditImageToolbar] = useCellValues(
69
+ const [ImagePlaceholderComponent, disableImageResize, allowSetImageDimensions, imagePreviewHandler, readOnly, EditImageToolbar] = useCellValues(
70
70
  imagePlaceholder$,
71
71
  disableImageResize$,
72
+ allowSetImageDimensions$,
72
73
  imagePreviewHandler$,
73
74
  readOnly$,
74
75
  editImageToolbarComponent$
@@ -143,6 +144,13 @@ function ImageEditor({ src, title, alt, nodeKey, width, height, rest }) {
143
144
  setImageSource(src);
144
145
  }
145
146
  }, [src, imagePreviewHandler, initialImagePath]);
147
+ React__default.useEffect(() => {
148
+ if (allowSetImageDimensions && imageRef.current) {
149
+ const { current: image } = imageRef;
150
+ syncDimensionWithImageResizer(image, "width", width);
151
+ syncDimensionWithImageResizer(image, "height", height);
152
+ }
153
+ }, [allowSetImageDimensions, width, height]);
146
154
  React__default.useEffect(() => {
147
155
  let isMounted = true;
148
156
  const unregister = mergeRegister(
@@ -249,10 +257,19 @@ function ImageEditor({ src, title, alt, nodeKey, width, height, rest }) {
249
257
  imageSource,
250
258
  initialImagePath,
251
259
  title: title ?? "",
252
- alt: alt ?? ""
260
+ alt: alt ?? "",
261
+ width,
262
+ height
253
263
  }
254
264
  ))) : null;
255
265
  }
266
+ const syncDimensionWithImageResizer = (image, key, value) => {
267
+ if (typeof value === "number") {
268
+ image.style[key] = `${value}px`;
269
+ } else {
270
+ image.style.removeProperty(key);
271
+ }
272
+ };
256
273
  export {
257
274
  ImageEditor
258
275
  };
@@ -13,7 +13,13 @@ import { MdastImageVisitor, MdastHtmlImageVisitor, MdastJsxImageVisitor } from "
13
13
  const internalInsertImage$ = Signal((r) => {
14
14
  r.sub(r.pipe(internalInsertImage$, withLatestFrom(activeEditor$)), ([values, theEditor]) => {
15
15
  theEditor == null ? void 0 : theEditor.update(() => {
16
- const imageNode = $createImageNode({ altText: values.altText ?? "", src: values.src, title: values.title ?? "" });
16
+ const imageNode = $createImageNode({
17
+ altText: values.altText ?? "",
18
+ src: values.src,
19
+ title: values.title ?? "",
20
+ width: parseImageDimension(values.width),
21
+ height: parseImageDimension(values.height)
22
+ });
17
23
  $insertNodes([imageNode]);
18
24
  if ($isRootOrShadowRoot(imageNode.getParentOrThrow())) {
19
25
  $wrapNodeInElement(imageNode, $createParagraphNode).selectEnd();
@@ -44,8 +50,8 @@ const imageDialogState$ = Cell(
44
50
  { type: "inactive" },
45
51
  (r) => {
46
52
  r.sub(
47
- r.pipe(saveImage$, withLatestFrom(activeEditor$, imageUploadHandler$, imageDialogState$)),
48
- ([values, theEditor, imageUploadHandler, dialogState]) => {
53
+ r.pipe(saveImage$, withLatestFrom(activeEditor$, imageUploadHandler$, imageDialogState$, allowSetImageDimensions$)),
54
+ ([values, theEditor, imageUploadHandler, dialogState, allowSetImageDimensions]) => {
49
55
  const handler = dialogState.type === "editing" ? (src) => {
50
56
  theEditor == null ? void 0 : theEditor.update(() => {
51
57
  const { nodeKey } = dialogState;
@@ -53,6 +59,11 @@ const imageDialogState$ = Cell(
53
59
  imageNode.setTitle(values.title);
54
60
  imageNode.setAltText(values.altText);
55
61
  imageNode.setSrc(src);
62
+ if (allowSetImageDimensions) {
63
+ const width = parseImageDimension(values.width);
64
+ const height = parseImageDimension(values.height);
65
+ imageNode.setWidthAndHeight(width ?? "inherit", height ?? "inherit");
66
+ }
56
67
  });
57
68
  r.pub(imageDialogState$, { type: "inactive" });
58
69
  } : (src) => {
@@ -158,8 +169,15 @@ const closeImageDialog$ = Action((r) => {
158
169
  r.link(r.pipe(closeImageDialog$, mapTo({ type: "inactive" })), imageDialogState$);
159
170
  });
160
171
  const disableImageSettingsButton$ = Cell(false);
172
+ const allowSetImageDimensions$ = Cell(false);
161
173
  const saveImage$ = Signal();
162
174
  const editImageToolbarComponent$ = Cell(EditImageToolbar);
175
+ const parseImageDimension = (value) => {
176
+ if (typeof value === "undefined")
177
+ return void 0;
178
+ const parsed = parseInt(String(value), 10);
179
+ return Number.isNaN(parsed) ? void 0 : parsed;
180
+ };
163
181
  const imagePlugin = realmPlugin({
164
182
  init(realm, params) {
165
183
  realm.pubIn({
@@ -171,6 +189,7 @@ const imagePlugin = realmPlugin({
171
189
  [imageAutocompleteSuggestions$]: (params == null ? void 0 : params.imageAutocompleteSuggestions) ?? [],
172
190
  [disableImageResize$]: Boolean(params == null ? void 0 : params.disableImageResize),
173
191
  [disableImageSettingsButton$]: Boolean(params == null ? void 0 : params.disableImageSettingsButton),
192
+ [allowSetImageDimensions$]: Boolean(params == null ? void 0 : params.allowSetImageDimensions),
174
193
  [imagePreviewHandler$]: (params == null ? void 0 : params.imagePreviewHandler) ?? null,
175
194
  [editImageToolbarComponent$]: (params == null ? void 0 : params.EditImageToolbar) ?? EditImageToolbar,
176
195
  [imagePlaceholder$]: (params == null ? void 0 : params.imagePlaceholder) ?? ImagePlaceholder
@@ -182,6 +201,7 @@ const imagePlugin = realmPlugin({
182
201
  [imageAutocompleteSuggestions$]: (params == null ? void 0 : params.imageAutocompleteSuggestions) ?? [],
183
202
  [disableImageResize$]: Boolean(params == null ? void 0 : params.disableImageResize),
184
203
  [imagePreviewHandler$]: (params == null ? void 0 : params.imagePreviewHandler) ?? null,
204
+ [allowSetImageDimensions$]: Boolean(params == null ? void 0 : params.allowSetImageDimensions),
185
205
  [editImageToolbarComponent$]: (params == null ? void 0 : params.EditImageToolbar) ?? EditImageToolbar,
186
206
  [imagePlaceholder$]: (params == null ? void 0 : params.imagePlaceholder) ?? ImagePlaceholder
187
207
  });
@@ -331,6 +351,7 @@ export {
331
351
  $isImageNode,
332
352
  INSERT_IMAGE_COMMAND,
333
353
  ImageNode,
354
+ allowSetImageDimensions$,
334
355
  closeImageDialog$,
335
356
  disableImageResize$,
336
357
  disableImageSettingsButton$,
@@ -344,5 +365,6 @@ export {
344
365
  insertImage$,
345
366
  openEditImageDialog$,
346
367
  openNewImageDialog$,
368
+ parseImageDimension,
347
369
  saveImage$
348
370
  };