@cyberyozh/text-editor 0.1.8 → 0.1.9

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/dist/editor.js CHANGED
@@ -816,12 +816,8 @@ var BubbleMenuContent = ({ editor, onLinkClick }) => {
816
816
  editor.state.selection.$from.pos,
817
817
  editor.state.selection.$to.pos
818
818
  );
819
- let linkUrl;
820
- if (isLink) {
821
- const link = editor.getAttributes("link");
822
- linkUrl = link.href;
823
- }
824
- onLinkClick?.(selectedText, linkUrl);
819
+ const linkAttrs = editor.getAttributes("link");
820
+ onLinkClick?.({ title: selectedText, ...linkAttrs });
825
821
  openDialogCommand("link");
826
822
  };
827
823
  return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
@@ -1279,7 +1275,7 @@ var CalloutMenu = ({ isOpen, onClose, onColorSelect, editor, nodePos }) => {
1279
1275
  const b = parseInt(bgColor.slice(5, 7), 16);
1280
1276
  const brightness = (r * 299 + g * 587 + b * 114) / 1e3;
1281
1277
  const textColor = brightness > 128 ? "#000000" : "#FFFFFF";
1282
- editor.chain().setTextSelection({ from, to }).setColor(textColor).setNodeSelection(nodePos).updateAttributes("callout", { bgColor }).run();
1278
+ editor.chain().setTextSelection({ from, to }).setColor(textColor).setNodeSelection(nodePos).updateAttributes("callout", { bgColor }).updateAttributes("calloutTip", { bgColor }).updateAttributes("calloutAlert", { bgColor }).run();
1283
1279
  }
1284
1280
  }
1285
1281
  },
@@ -1313,7 +1309,7 @@ var DragHandle = ({ editor }) => {
1313
1309
  currentNodePosRef.current = pos;
1314
1310
  try {
1315
1311
  const node = editor.view.state.doc.nodeAt(pos);
1316
- const isCallout = node?.type.name === "callout";
1312
+ const isCallout = node?.type.name === "callout" || node?.type.name === "calloutAlert" || node?.type.name === "calloutTip";
1317
1313
  setIsCalloutNode(isCallout);
1318
1314
  if (prevPos !== null && prevPos !== pos) {
1319
1315
  setShowCalloutMenu(false);
@@ -1341,7 +1337,7 @@ var DragHandle = ({ editor }) => {
1341
1337
  if (node) {
1342
1338
  const from = pos + 1;
1343
1339
  const to = pos + node.nodeSize - 1;
1344
- editor.chain().setTextSelection({ from, to }).setColor(textColor).setNodeSelection(pos).updateAttributes("callout", { bgColor }).run();
1340
+ editor.chain().setTextSelection({ from, to }).setColor(textColor).setNodeSelection(pos).updateAttributes("callout", { bgColor }).updateAttributes("calloutTip", { bgColor }).updateAttributes("calloutAlert", { bgColor }).run();
1345
1341
  }
1346
1342
  setShowCalloutMenu(false);
1347
1343
  }
@@ -2010,7 +2006,8 @@ var ImageDialog = ({
2010
2006
  title: formState.title,
2011
2007
  caption: formState.caption,
2012
2008
  width: formState.width,
2013
- height: formState.height
2009
+ height: formState.height,
2010
+ storageKey: result.storageKey
2014
2011
  });
2015
2012
  handleClose();
2016
2013
  } finally {
@@ -2262,24 +2259,32 @@ var AddLinkDialog = ({
2262
2259
  open,
2263
2260
  onClose,
2264
2261
  onLinkAdd,
2265
- initialTitle = "",
2266
- initialUrl = ""
2262
+ linkDialogContext
2267
2263
  }) => {
2268
2264
  const translations2 = useTiptapTranslations();
2269
- const [title, setTitle] = (0, import_react15.useState)(initialTitle);
2270
- const [url, setUrl] = (0, import_react15.useState)(initialUrl);
2265
+ const initialLinkTarget = linkDialogContext?.target ? linkDialogContext.target === "_blank" : true;
2266
+ const initialLinkRel = linkDialogContext?.rel ? linkDialogContext.rel?.includes("noopener") : true;
2267
+ const [title, setTitle] = (0, import_react15.useState)(linkDialogContext?.title);
2268
+ const [url, setUrl] = (0, import_react15.useState)(linkDialogContext?.href);
2269
+ const [openInNewTab, setOpenInNewTab] = (0, import_react15.useState)(initialLinkTarget);
2270
+ const [addNoopener, setAddNoopener] = (0, import_react15.useState)(initialLinkRel);
2271
2271
  const [error, setError] = (0, import_react15.useState)("");
2272
2272
  const titleInputRef = (0, import_react15.useRef)(null);
2273
+ const clearDialogState = () => {
2274
+ setTitle(linkDialogContext?.title);
2275
+ setUrl(linkDialogContext?.href);
2276
+ setOpenInNewTab(initialLinkTarget);
2277
+ setAddNoopener(initialLinkRel);
2278
+ setError("");
2279
+ };
2273
2280
  (0, import_react15.useEffect)(() => {
2274
2281
  if (open) {
2275
- setTitle(initialTitle);
2276
- setUrl(initialUrl);
2277
- setError("");
2282
+ clearDialogState();
2278
2283
  setTimeout(() => {
2279
2284
  titleInputRef.current?.focus();
2280
2285
  }, 0);
2281
2286
  }
2282
- }, [open, initialTitle, initialUrl]);
2287
+ }, [open, linkDialogContext]);
2283
2288
  const validateUrl = (urlStr) => {
2284
2289
  if (!urlStr.trim()) {
2285
2290
  setError(translations2.link.emptyUrl);
@@ -2305,22 +2310,22 @@ var AddLinkDialog = ({
2305
2310
  };
2306
2311
  const handleSubmit = () => {
2307
2312
  setError("");
2308
- if (!title.trim()) {
2313
+ if (!title?.trim()) {
2309
2314
  setError(translations2.link.titleError);
2310
2315
  return;
2311
2316
  }
2312
- if (!validateUrl(url)) {
2317
+ if (!validateUrl(url ?? "")) {
2313
2318
  return;
2314
2319
  }
2315
- const hasProtocol = url.includes("://") || url.startsWith("mailto:") || url.startsWith("tel:");
2320
+ const hasProtocol = url?.includes("://") || url?.startsWith("mailto:") || url?.startsWith("tel:");
2316
2321
  const finalUrl = hasProtocol ? url : `https://${url}`;
2317
- onLinkAdd(title, finalUrl);
2322
+ const target = openInNewTab ? "_blank" : "_self";
2323
+ const rel = addNoopener ? "noopener noreferrer nofollow" : null;
2324
+ onLinkAdd({ title, href: finalUrl ?? "", target, rel });
2318
2325
  handleClose();
2319
2326
  };
2320
2327
  const handleClose = () => {
2321
- setTitle(initialTitle);
2322
- setUrl(initialUrl);
2323
- setError("");
2328
+ clearDialogState();
2324
2329
  onClose();
2325
2330
  };
2326
2331
  const handleKeyDown = (e) => {
@@ -2335,7 +2340,7 @@ var AddLinkDialog = ({
2335
2340
  open,
2336
2341
  handleClose,
2337
2342
  children: [
2338
- /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("p", { className: "link-dialog-title", children: initialUrl ? translations2.link.editLink : translations2.link.addLink }),
2343
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("p", { className: "link-dialog-title", children: linkDialogContext?.href ? translations2.link.editLink : translations2.link.addLink }),
2339
2344
  /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("hr", { className: "link-dialog-divider" }),
2340
2345
  /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "link-dialog-content", children: [
2341
2346
  /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "link-dialog-form-group", children: [
@@ -2377,12 +2382,40 @@ var AddLinkDialog = ({
2377
2382
  type: "text",
2378
2383
  className: "link-dialog-input",
2379
2384
  placeholder: translations2.link.urlPlaceholder,
2380
- value: url,
2385
+ value: url ?? "",
2381
2386
  onChange: (e) => setUrl(e.target.value),
2382
2387
  onKeyDown: handleKeyDown
2383
2388
  }
2384
2389
  )
2385
2390
  ] }),
2391
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "link-dialog-form-group", children: [
2392
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("label", { className: "link-dialog-checkbox-label", children: [
2393
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(
2394
+ "input",
2395
+ {
2396
+ type: "checkbox",
2397
+ checked: openInNewTab,
2398
+ onChange: (e) => setOpenInNewTab(e.target.checked)
2399
+ }
2400
+ ),
2401
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { children: translations2.link.openInNewTab })
2402
+ ] }),
2403
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("p", { className: "link-dialog-help-text", children: translations2.link.openInNewTabHelp })
2404
+ ] }),
2405
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "link-dialog-form-group", children: [
2406
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("label", { className: "link-dialog-checkbox-label", children: [
2407
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(
2408
+ "input",
2409
+ {
2410
+ type: "checkbox",
2411
+ checked: addNoopener,
2412
+ onChange: (e) => setAddNoopener(e.target.checked)
2413
+ }
2414
+ ),
2415
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { children: translations2.link.addNofollow })
2416
+ ] }),
2417
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("p", { className: "link-dialog-help-text", children: translations2.link.addNofollowHelp })
2418
+ ] }),
2386
2419
  error && /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("div", { className: "link-dialog-error", children: error })
2387
2420
  ] }),
2388
2421
  /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "link-dialog-actions", children: [
@@ -2401,7 +2434,7 @@ var AddLinkDialog = ({
2401
2434
  className: "link-dialog-button-submit",
2402
2435
  onClick: handleSubmit,
2403
2436
  type: "button",
2404
- children: initialUrl ? translations2.link.editLink : translations2.link.addLink
2437
+ children: linkDialogContext?.href ? translations2.link.editLink : translations2.link.addLink
2405
2438
  }
2406
2439
  )
2407
2440
  ] })
@@ -3248,6 +3281,13 @@ var BaseFigure = import_extension_image.default.extend({
3248
3281
  caption: {
3249
3282
  default: null,
3250
3283
  parseHTML: (element) => element.querySelector("figcaption")?.textContent || null
3284
+ },
3285
+ storageKey: {
3286
+ default: null,
3287
+ parseHTML: (element) => element.getAttribute("data-storage-key"),
3288
+ renderHTML: (attributes) => ({
3289
+ "data-storage-key": attributes.storageKey
3290
+ })
3251
3291
  }
3252
3292
  };
3253
3293
  },
@@ -3338,6 +3378,32 @@ var BaseLegacyBookAlert = import_core8.Node.create({
3338
3378
  return {
3339
3379
  type: {
3340
3380
  default: "error"
3381
+ },
3382
+ bgColor: {
3383
+ default: null,
3384
+ parseHTML: (element) => {
3385
+ if (typeof element === "string") return null;
3386
+ return element.getAttribute("data-bg-color");
3387
+ },
3388
+ renderHTML: (attributes) => {
3389
+ if (!attributes.bgColor) return {};
3390
+ return {
3391
+ "data-bg-color": attributes.bgColor
3392
+ };
3393
+ }
3394
+ },
3395
+ emoji: {
3396
+ default: null,
3397
+ parseHTML: (element) => {
3398
+ if (typeof element === "string") return null;
3399
+ return element.getAttribute("data-emoji");
3400
+ },
3401
+ renderHTML: (attributes) => {
3402
+ if (!attributes.emoji) return {};
3403
+ return {
3404
+ "data-emoji": attributes.emoji
3405
+ };
3406
+ }
3341
3407
  }
3342
3408
  };
3343
3409
  },
@@ -3367,6 +3433,32 @@ var BaseLegacyBookTip = import_core8.Node.create({
3367
3433
  return {
3368
3434
  type: {
3369
3435
  default: "info"
3436
+ },
3437
+ bgColor: {
3438
+ default: null,
3439
+ parseHTML: (element) => {
3440
+ if (typeof element === "string") return null;
3441
+ return element.getAttribute("data-bg-color");
3442
+ },
3443
+ renderHTML: (attributes) => {
3444
+ if (!attributes.bgColor) return {};
3445
+ return {
3446
+ "data-bg-color": attributes.bgColor
3447
+ };
3448
+ }
3449
+ },
3450
+ emoji: {
3451
+ default: null,
3452
+ parseHTML: (element) => {
3453
+ if (typeof element === "string") return null;
3454
+ return element.getAttribute("data-emoji");
3455
+ },
3456
+ renderHTML: (attributes) => {
3457
+ if (!attributes.emoji) return {};
3458
+ return {
3459
+ "data-emoji": attributes.emoji
3460
+ };
3461
+ }
3370
3462
  }
3371
3463
  };
3372
3464
  },
@@ -4498,7 +4590,7 @@ var CustomQuoteSignView = (props) => {
4498
4590
  var import_react25 = require("@tiptap/react");
4499
4591
  var import_jsx_runtime89 = require("react/jsx-runtime");
4500
4592
  var FigureView = ({ node, getPos, editor }) => {
4501
- const { fileId, src, alt, title, caption, width, height } = node.attrs;
4593
+ const { fileId, src, alt, title, caption, width, height, storageKey } = node.attrs;
4502
4594
  const imageExtension = editor.extensionManager.extensions.find((ext) => ext.name === "image");
4503
4595
  const getImageById = imageExtension?.options.getImageById;
4504
4596
  const onFigureClick = imageExtension?.options.onFigureClick;
@@ -5188,7 +5280,6 @@ var createHandleImageAdd = (editor, closeDialogCommand, savedPos) => {
5188
5280
  };
5189
5281
  var createHandleImageEdit = (editor, closeDialogCommand, pos) => {
5190
5282
  return (attrs) => {
5191
- console.log("\u{1F440} ~ createHandleImageEdit ~ attrs:", attrs);
5192
5283
  if (!editor || pos === void 0) return;
5193
5284
  editor.chain().focus().setNodeSelection(pos).updateFigure(attrs).run();
5194
5285
  closeDialogCommand();
@@ -5214,30 +5305,30 @@ var createHandleVideoAdd = (editor, closeDialogCommand, savedPos) => {
5214
5305
  closeDialogCommand();
5215
5306
  };
5216
5307
  };
5217
- var createHandleLinkAdd = (editor, closeDialogCommand, savedPos) => {
5218
- return (title, url) => {
5219
- if (!editor) return;
5220
- const hasSelection = !editor.state.selection.empty;
5221
- if (hasSelection) {
5222
- editor.chain().focus().command(({ tr, state }) => {
5223
- const { from, to } = state.selection;
5224
- tr.insertText(title, from, to);
5225
- tr.addMark(from, from + title.length, editor.schema.marks.link.create({ href: url }));
5226
- return true;
5227
- }).run();
5228
- } else {
5229
- const { state } = editor;
5230
- const insertPos = savedPos ?? state.selection.from;
5231
- editor.chain().focus().command(({ tr }) => {
5232
- const linkMark = editor.schema.marks.link.create({ href: url });
5233
- const textNode = editor.schema.text(title, [linkMark]);
5234
- tr.insert(insertPos, textNode);
5235
- tr.setSelection(import_state5.TextSelection.create(tr.doc, insertPos, insertPos + title.length));
5236
- return true;
5237
- }).run();
5238
- }
5239
- closeDialogCommand();
5240
- };
5308
+ var createHandleLinkAdd = (editor, closeDialogCommand, linkParams, savedPos) => {
5309
+ if (!editor) return;
5310
+ const hasSelection = !editor.state.selection.empty;
5311
+ const titleLength = linkParams.title?.length ?? 0;
5312
+ if (hasSelection) {
5313
+ editor.chain().focus().command(({ tr, state }) => {
5314
+ const { from, to } = state.selection;
5315
+ tr.insertText(linkParams.title ?? "", from, to);
5316
+ tr.addMark(from, from + titleLength, editor.schema.marks.link.create({ ...linkParams }));
5317
+ return true;
5318
+ }).run();
5319
+ } else {
5320
+ const { state } = editor;
5321
+ const insertPos = savedPos ?? state.selection.from;
5322
+ const titleLength2 = linkParams.title?.length ?? 0;
5323
+ editor.chain().focus().command(({ tr }) => {
5324
+ const linkMark = editor.schema.marks.link.create({ ...linkParams });
5325
+ const textNode = editor.schema.text(linkParams.title ?? "", [linkMark]);
5326
+ tr.insert(insertPos, textNode);
5327
+ tr.setSelection(import_state5.TextSelection.create(tr.doc, insertPos, insertPos + titleLength2));
5328
+ return true;
5329
+ }).run();
5330
+ }
5331
+ closeDialogCommand();
5241
5332
  };
5242
5333
  var createHandleKatexAdd = (editor, closeDialogCommand, savedPos) => {
5243
5334
  return (latex, pos) => {
@@ -5704,7 +5795,11 @@ var ru_default = {
5704
5795
  invalidUrl: "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 URL",
5705
5796
  emptyUrl: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 URL \u0441\u0441\u044B\u043B\u043A\u0438",
5706
5797
  addLinkTitle: "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0441\u0441\u044B\u043B\u043A\u0443",
5707
- removeLinkTitle: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0441\u044B\u043B\u043A\u0443"
5798
+ removeLinkTitle: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0441\u044B\u043B\u043A\u0443",
5799
+ openInNewTab: "\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0432 \u043D\u043E\u0432\u043E\u0439 \u0432\u043A\u043B\u0430\u0434\u043A\u0435",
5800
+ openInNewTabHelp: "\u0421\u0441\u044B\u043B\u043A\u0430 \u043E\u0442\u043A\u0440\u043E\u0435\u0442\u0441\u044F \u0432 \u043D\u043E\u0432\u043E\u043C \u043E\u043A\u043D\u0435 \u0438\u043B\u0438 \u0432\u043A\u043B\u0430\u0434\u043A\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430",
5801
+ addNofollow: '\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C rel="nofollow"',
5802
+ addNofollowHelp: "\u0423\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u043F\u043E\u0438\u0441\u043A\u043E\u0432\u044B\u043C \u0441\u0438\u0441\u0442\u0435\u043C\u0430\u043C \u043D\u0435 \u0443\u0447\u0438\u0442\u044B\u0432\u0430\u0442\u044C \u0441\u0441\u044B\u043B\u043A\u0443 \u043F\u0440\u0438 \u0440\u0430\u0441\u0447\u0435\u0442\u0435 \u0440\u0435\u0439\u0442\u0438\u043D\u0433\u0430 \u0438 \u0438\u043D\u0434\u0435\u043A\u0441\u0430\u0446\u0438\u0438 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B, \u043D\u0430 \u043A\u043E\u0442\u043E\u0440\u043E\u0439 \u043E\u043D\u0438 \u0440\u0430\u0437\u043C\u0435\u0449\u0435\u043D\u044B. \u0420\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0443\u0435\u0442\u0441\u044F \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u0442\u044C \u043A\u043E \u0432\u0441\u0435\u043C \u0441\u0441\u044B\u043B\u043A\u0430\u043C \u043D\u0430 \u0441\u0442\u043E\u0440\u043E\u043D\u043D\u0438\u0435 \u0441\u0430\u0439\u0442\u044B."
5708
5803
  },
5709
5804
  lists: {
5710
5805
  bulletList: "\u041D\u0435\u043D\u0443\u043C\u0435\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0441\u043F\u0438\u0441\u043E\u043A",
@@ -5864,7 +5959,11 @@ var en_default = {
5864
5959
  invalidUrl: "Invalid URL",
5865
5960
  emptyUrl: "Enter link URL",
5866
5961
  addLinkTitle: "Add link",
5867
- removeLinkTitle: "Remove link"
5962
+ removeLinkTitle: "Remove link",
5963
+ openInNewTab: "Open in new tab",
5964
+ openInNewTabHelp: "Link will open in a new browser window or tab",
5965
+ addNofollow: 'Add rel="nofollow"',
5966
+ addNofollowHelp: "Tells search engines not to count this link when calculating page rankings and indexing. Recommended for all external links."
5868
5967
  },
5869
5968
  lists: {
5870
5969
  bulletList: "Bullet list",
@@ -6060,6 +6159,7 @@ function TiptapContent({
6060
6159
  return false;
6061
6160
  }
6062
6161
  },
6162
+ onCreate: ({ editor: editor2 }) => handleChange?.(editor2.getJSON()),
6063
6163
  onBlur: ({ editor: editor2 }) => handleChange?.(editor2.getJSON())
6064
6164
  },
6065
6165
  [lang]
@@ -6084,8 +6184,8 @@ function TiptapContent({
6084
6184
  const handleVideoAdd = (url) => {
6085
6185
  createHandleVideoAdd(editor, handleCloseDialog, savedDialogCursorPos)(url);
6086
6186
  };
6087
- const handleLinkAdd = (title, url) => {
6088
- createHandleLinkAdd(editor, handleCloseDialog, savedDialogCursorPos)(title, url);
6187
+ const handleLinkAdd = (params) => {
6188
+ createHandleLinkAdd(editor, handleCloseDialog, params, savedDialogCursorPos);
6089
6189
  };
6090
6190
  const handleKatexAdd = (latex) => {
6091
6191
  const katexHandler = createHandleKatexAdd(editor, handleCloseDialog, savedDialogCursorPos);
@@ -6123,7 +6223,7 @@ function TiptapContent({
6123
6223
  BubbleMenuContent,
6124
6224
  {
6125
6225
  editor,
6126
- onLinkClick: (selectedText, url) => setLinkDialogContext({ selectedText, url })
6226
+ onLinkClick: (params) => setLinkDialogContext(params)
6127
6227
  }
6128
6228
  )
6129
6229
  }
@@ -6159,8 +6259,7 @@ function TiptapContent({
6159
6259
  open: true,
6160
6260
  onClose: handleCloseDialog,
6161
6261
  onLinkAdd: handleLinkAdd,
6162
- initialTitle: linkDialogContext?.selectedText || "",
6163
- initialUrl: linkDialogContext?.url || ""
6262
+ linkDialogContext
6164
6263
  }
6165
6264
  ),
6166
6265
  activeDialog === "video" && /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(