@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.mjs CHANGED
@@ -780,12 +780,8 @@ var BubbleMenuContent = ({ editor, onLinkClick }) => {
780
780
  editor.state.selection.$from.pos,
781
781
  editor.state.selection.$to.pos
782
782
  );
783
- let linkUrl;
784
- if (isLink) {
785
- const link = editor.getAttributes("link");
786
- linkUrl = link.href;
787
- }
788
- onLinkClick?.(selectedText, linkUrl);
783
+ const linkAttrs = editor.getAttributes("link");
784
+ onLinkClick?.({ title: selectedText, ...linkAttrs });
789
785
  openDialogCommand("link");
790
786
  };
791
787
  return /* @__PURE__ */ jsxs10(
@@ -1243,7 +1239,7 @@ var CalloutMenu = ({ isOpen, onClose, onColorSelect, editor, nodePos }) => {
1243
1239
  const b = parseInt(bgColor.slice(5, 7), 16);
1244
1240
  const brightness = (r * 299 + g * 587 + b * 114) / 1e3;
1245
1241
  const textColor = brightness > 128 ? "#000000" : "#FFFFFF";
1246
- editor.chain().setTextSelection({ from, to }).setColor(textColor).setNodeSelection(nodePos).updateAttributes("callout", { bgColor }).run();
1242
+ editor.chain().setTextSelection({ from, to }).setColor(textColor).setNodeSelection(nodePos).updateAttributes("callout", { bgColor }).updateAttributes("calloutTip", { bgColor }).updateAttributes("calloutAlert", { bgColor }).run();
1247
1243
  }
1248
1244
  }
1249
1245
  },
@@ -1277,7 +1273,7 @@ var DragHandle = ({ editor }) => {
1277
1273
  currentNodePosRef.current = pos;
1278
1274
  try {
1279
1275
  const node = editor.view.state.doc.nodeAt(pos);
1280
- const isCallout = node?.type.name === "callout";
1276
+ const isCallout = node?.type.name === "callout" || node?.type.name === "calloutAlert" || node?.type.name === "calloutTip";
1281
1277
  setIsCalloutNode(isCallout);
1282
1278
  if (prevPos !== null && prevPos !== pos) {
1283
1279
  setShowCalloutMenu(false);
@@ -1305,7 +1301,7 @@ var DragHandle = ({ editor }) => {
1305
1301
  if (node) {
1306
1302
  const from = pos + 1;
1307
1303
  const to = pos + node.nodeSize - 1;
1308
- editor.chain().setTextSelection({ from, to }).setColor(textColor).setNodeSelection(pos).updateAttributes("callout", { bgColor }).run();
1304
+ editor.chain().setTextSelection({ from, to }).setColor(textColor).setNodeSelection(pos).updateAttributes("callout", { bgColor }).updateAttributes("calloutTip", { bgColor }).updateAttributes("calloutAlert", { bgColor }).run();
1309
1305
  }
1310
1306
  setShowCalloutMenu(false);
1311
1307
  }
@@ -1974,7 +1970,8 @@ var ImageDialog = ({
1974
1970
  title: formState.title,
1975
1971
  caption: formState.caption,
1976
1972
  width: formState.width,
1977
- height: formState.height
1973
+ height: formState.height,
1974
+ storageKey: result.storageKey
1978
1975
  });
1979
1976
  handleClose();
1980
1977
  } finally {
@@ -2226,24 +2223,32 @@ var AddLinkDialog = ({
2226
2223
  open,
2227
2224
  onClose,
2228
2225
  onLinkAdd,
2229
- initialTitle = "",
2230
- initialUrl = ""
2226
+ linkDialogContext
2231
2227
  }) => {
2232
2228
  const translations2 = useTiptapTranslations();
2233
- const [title, setTitle] = useState8(initialTitle);
2234
- const [url, setUrl] = useState8(initialUrl);
2229
+ const initialLinkTarget = linkDialogContext?.target ? linkDialogContext.target === "_blank" : true;
2230
+ const initialLinkRel = linkDialogContext?.rel ? linkDialogContext.rel?.includes("noopener") : true;
2231
+ const [title, setTitle] = useState8(linkDialogContext?.title);
2232
+ const [url, setUrl] = useState8(linkDialogContext?.href);
2233
+ const [openInNewTab, setOpenInNewTab] = useState8(initialLinkTarget);
2234
+ const [addNoopener, setAddNoopener] = useState8(initialLinkRel);
2235
2235
  const [error, setError] = useState8("");
2236
2236
  const titleInputRef = useRef8(null);
2237
+ const clearDialogState = () => {
2238
+ setTitle(linkDialogContext?.title);
2239
+ setUrl(linkDialogContext?.href);
2240
+ setOpenInNewTab(initialLinkTarget);
2241
+ setAddNoopener(initialLinkRel);
2242
+ setError("");
2243
+ };
2237
2244
  useEffect6(() => {
2238
2245
  if (open) {
2239
- setTitle(initialTitle);
2240
- setUrl(initialUrl);
2241
- setError("");
2246
+ clearDialogState();
2242
2247
  setTimeout(() => {
2243
2248
  titleInputRef.current?.focus();
2244
2249
  }, 0);
2245
2250
  }
2246
- }, [open, initialTitle, initialUrl]);
2251
+ }, [open, linkDialogContext]);
2247
2252
  const validateUrl = (urlStr) => {
2248
2253
  if (!urlStr.trim()) {
2249
2254
  setError(translations2.link.emptyUrl);
@@ -2269,22 +2274,22 @@ var AddLinkDialog = ({
2269
2274
  };
2270
2275
  const handleSubmit = () => {
2271
2276
  setError("");
2272
- if (!title.trim()) {
2277
+ if (!title?.trim()) {
2273
2278
  setError(translations2.link.titleError);
2274
2279
  return;
2275
2280
  }
2276
- if (!validateUrl(url)) {
2281
+ if (!validateUrl(url ?? "")) {
2277
2282
  return;
2278
2283
  }
2279
- const hasProtocol = url.includes("://") || url.startsWith("mailto:") || url.startsWith("tel:");
2284
+ const hasProtocol = url?.includes("://") || url?.startsWith("mailto:") || url?.startsWith("tel:");
2280
2285
  const finalUrl = hasProtocol ? url : `https://${url}`;
2281
- onLinkAdd(title, finalUrl);
2286
+ const target = openInNewTab ? "_blank" : "_self";
2287
+ const rel = addNoopener ? "noopener noreferrer nofollow" : null;
2288
+ onLinkAdd({ title, href: finalUrl ?? "", target, rel });
2282
2289
  handleClose();
2283
2290
  };
2284
2291
  const handleClose = () => {
2285
- setTitle(initialTitle);
2286
- setUrl(initialUrl);
2287
- setError("");
2292
+ clearDialogState();
2288
2293
  onClose();
2289
2294
  };
2290
2295
  const handleKeyDown = (e) => {
@@ -2299,7 +2304,7 @@ var AddLinkDialog = ({
2299
2304
  open,
2300
2305
  handleClose,
2301
2306
  children: [
2302
- /* @__PURE__ */ jsx69("p", { className: "link-dialog-title", children: initialUrl ? translations2.link.editLink : translations2.link.addLink }),
2307
+ /* @__PURE__ */ jsx69("p", { className: "link-dialog-title", children: linkDialogContext?.href ? translations2.link.editLink : translations2.link.addLink }),
2303
2308
  /* @__PURE__ */ jsx69("hr", { className: "link-dialog-divider" }),
2304
2309
  /* @__PURE__ */ jsxs20("div", { className: "link-dialog-content", children: [
2305
2310
  /* @__PURE__ */ jsxs20("div", { className: "link-dialog-form-group", children: [
@@ -2341,12 +2346,40 @@ var AddLinkDialog = ({
2341
2346
  type: "text",
2342
2347
  className: "link-dialog-input",
2343
2348
  placeholder: translations2.link.urlPlaceholder,
2344
- value: url,
2349
+ value: url ?? "",
2345
2350
  onChange: (e) => setUrl(e.target.value),
2346
2351
  onKeyDown: handleKeyDown
2347
2352
  }
2348
2353
  )
2349
2354
  ] }),
2355
+ /* @__PURE__ */ jsxs20("div", { className: "link-dialog-form-group", children: [
2356
+ /* @__PURE__ */ jsxs20("label", { className: "link-dialog-checkbox-label", children: [
2357
+ /* @__PURE__ */ jsx69(
2358
+ "input",
2359
+ {
2360
+ type: "checkbox",
2361
+ checked: openInNewTab,
2362
+ onChange: (e) => setOpenInNewTab(e.target.checked)
2363
+ }
2364
+ ),
2365
+ /* @__PURE__ */ jsx69("span", { children: translations2.link.openInNewTab })
2366
+ ] }),
2367
+ /* @__PURE__ */ jsx69("p", { className: "link-dialog-help-text", children: translations2.link.openInNewTabHelp })
2368
+ ] }),
2369
+ /* @__PURE__ */ jsxs20("div", { className: "link-dialog-form-group", children: [
2370
+ /* @__PURE__ */ jsxs20("label", { className: "link-dialog-checkbox-label", children: [
2371
+ /* @__PURE__ */ jsx69(
2372
+ "input",
2373
+ {
2374
+ type: "checkbox",
2375
+ checked: addNoopener,
2376
+ onChange: (e) => setAddNoopener(e.target.checked)
2377
+ }
2378
+ ),
2379
+ /* @__PURE__ */ jsx69("span", { children: translations2.link.addNofollow })
2380
+ ] }),
2381
+ /* @__PURE__ */ jsx69("p", { className: "link-dialog-help-text", children: translations2.link.addNofollowHelp })
2382
+ ] }),
2350
2383
  error && /* @__PURE__ */ jsx69("div", { className: "link-dialog-error", children: error })
2351
2384
  ] }),
2352
2385
  /* @__PURE__ */ jsxs20("div", { className: "link-dialog-actions", children: [
@@ -2365,7 +2398,7 @@ var AddLinkDialog = ({
2365
2398
  className: "link-dialog-button-submit",
2366
2399
  onClick: handleSubmit,
2367
2400
  type: "button",
2368
- children: initialUrl ? translations2.link.editLink : translations2.link.addLink
2401
+ children: linkDialogContext?.href ? translations2.link.editLink : translations2.link.addLink
2369
2402
  }
2370
2403
  )
2371
2404
  ] })
@@ -3212,6 +3245,13 @@ var BaseFigure = Image2.extend({
3212
3245
  caption: {
3213
3246
  default: null,
3214
3247
  parseHTML: (element) => element.querySelector("figcaption")?.textContent || null
3248
+ },
3249
+ storageKey: {
3250
+ default: null,
3251
+ parseHTML: (element) => element.getAttribute("data-storage-key"),
3252
+ renderHTML: (attributes) => ({
3253
+ "data-storage-key": attributes.storageKey
3254
+ })
3215
3255
  }
3216
3256
  };
3217
3257
  },
@@ -3302,6 +3342,32 @@ var BaseLegacyBookAlert = Node6.create({
3302
3342
  return {
3303
3343
  type: {
3304
3344
  default: "error"
3345
+ },
3346
+ bgColor: {
3347
+ default: null,
3348
+ parseHTML: (element) => {
3349
+ if (typeof element === "string") return null;
3350
+ return element.getAttribute("data-bg-color");
3351
+ },
3352
+ renderHTML: (attributes) => {
3353
+ if (!attributes.bgColor) return {};
3354
+ return {
3355
+ "data-bg-color": attributes.bgColor
3356
+ };
3357
+ }
3358
+ },
3359
+ emoji: {
3360
+ default: null,
3361
+ parseHTML: (element) => {
3362
+ if (typeof element === "string") return null;
3363
+ return element.getAttribute("data-emoji");
3364
+ },
3365
+ renderHTML: (attributes) => {
3366
+ if (!attributes.emoji) return {};
3367
+ return {
3368
+ "data-emoji": attributes.emoji
3369
+ };
3370
+ }
3305
3371
  }
3306
3372
  };
3307
3373
  },
@@ -3331,6 +3397,32 @@ var BaseLegacyBookTip = Node6.create({
3331
3397
  return {
3332
3398
  type: {
3333
3399
  default: "info"
3400
+ },
3401
+ bgColor: {
3402
+ default: null,
3403
+ parseHTML: (element) => {
3404
+ if (typeof element === "string") return null;
3405
+ return element.getAttribute("data-bg-color");
3406
+ },
3407
+ renderHTML: (attributes) => {
3408
+ if (!attributes.bgColor) return {};
3409
+ return {
3410
+ "data-bg-color": attributes.bgColor
3411
+ };
3412
+ }
3413
+ },
3414
+ emoji: {
3415
+ default: null,
3416
+ parseHTML: (element) => {
3417
+ if (typeof element === "string") return null;
3418
+ return element.getAttribute("data-emoji");
3419
+ },
3420
+ renderHTML: (attributes) => {
3421
+ if (!attributes.emoji) return {};
3422
+ return {
3423
+ "data-emoji": attributes.emoji
3424
+ };
3425
+ }
3334
3426
  }
3335
3427
  };
3336
3428
  },
@@ -4462,7 +4554,7 @@ var CustomQuoteSignView = (props) => {
4462
4554
  import { NodeViewWrapper as NodeViewWrapper4 } from "@tiptap/react";
4463
4555
  import { jsx as jsx89 } from "react/jsx-runtime";
4464
4556
  var FigureView = ({ node, getPos, editor }) => {
4465
- const { fileId, src, alt, title, caption, width, height } = node.attrs;
4557
+ const { fileId, src, alt, title, caption, width, height, storageKey } = node.attrs;
4466
4558
  const imageExtension = editor.extensionManager.extensions.find((ext) => ext.name === "image");
4467
4559
  const getImageById = imageExtension?.options.getImageById;
4468
4560
  const onFigureClick = imageExtension?.options.onFigureClick;
@@ -5152,7 +5244,6 @@ var createHandleImageAdd = (editor, closeDialogCommand, savedPos) => {
5152
5244
  };
5153
5245
  var createHandleImageEdit = (editor, closeDialogCommand, pos) => {
5154
5246
  return (attrs) => {
5155
- console.log("\u{1F440} ~ createHandleImageEdit ~ attrs:", attrs);
5156
5247
  if (!editor || pos === void 0) return;
5157
5248
  editor.chain().focus().setNodeSelection(pos).updateFigure(attrs).run();
5158
5249
  closeDialogCommand();
@@ -5178,30 +5269,30 @@ var createHandleVideoAdd = (editor, closeDialogCommand, savedPos) => {
5178
5269
  closeDialogCommand();
5179
5270
  };
5180
5271
  };
5181
- var createHandleLinkAdd = (editor, closeDialogCommand, savedPos) => {
5182
- return (title, url) => {
5183
- if (!editor) return;
5184
- const hasSelection = !editor.state.selection.empty;
5185
- if (hasSelection) {
5186
- editor.chain().focus().command(({ tr, state }) => {
5187
- const { from, to } = state.selection;
5188
- tr.insertText(title, from, to);
5189
- tr.addMark(from, from + title.length, editor.schema.marks.link.create({ href: url }));
5190
- return true;
5191
- }).run();
5192
- } else {
5193
- const { state } = editor;
5194
- const insertPos = savedPos ?? state.selection.from;
5195
- editor.chain().focus().command(({ tr }) => {
5196
- const linkMark = editor.schema.marks.link.create({ href: url });
5197
- const textNode = editor.schema.text(title, [linkMark]);
5198
- tr.insert(insertPos, textNode);
5199
- tr.setSelection(TextSelection5.create(tr.doc, insertPos, insertPos + title.length));
5200
- return true;
5201
- }).run();
5202
- }
5203
- closeDialogCommand();
5204
- };
5272
+ var createHandleLinkAdd = (editor, closeDialogCommand, linkParams, savedPos) => {
5273
+ if (!editor) return;
5274
+ const hasSelection = !editor.state.selection.empty;
5275
+ const titleLength = linkParams.title?.length ?? 0;
5276
+ if (hasSelection) {
5277
+ editor.chain().focus().command(({ tr, state }) => {
5278
+ const { from, to } = state.selection;
5279
+ tr.insertText(linkParams.title ?? "", from, to);
5280
+ tr.addMark(from, from + titleLength, editor.schema.marks.link.create({ ...linkParams }));
5281
+ return true;
5282
+ }).run();
5283
+ } else {
5284
+ const { state } = editor;
5285
+ const insertPos = savedPos ?? state.selection.from;
5286
+ const titleLength2 = linkParams.title?.length ?? 0;
5287
+ editor.chain().focus().command(({ tr }) => {
5288
+ const linkMark = editor.schema.marks.link.create({ ...linkParams });
5289
+ const textNode = editor.schema.text(linkParams.title ?? "", [linkMark]);
5290
+ tr.insert(insertPos, textNode);
5291
+ tr.setSelection(TextSelection5.create(tr.doc, insertPos, insertPos + titleLength2));
5292
+ return true;
5293
+ }).run();
5294
+ }
5295
+ closeDialogCommand();
5205
5296
  };
5206
5297
  var createHandleKatexAdd = (editor, closeDialogCommand, savedPos) => {
5207
5298
  return (latex, pos) => {
@@ -5668,7 +5759,11 @@ var ru_default = {
5668
5759
  invalidUrl: "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 URL",
5669
5760
  emptyUrl: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 URL \u0441\u0441\u044B\u043B\u043A\u0438",
5670
5761
  addLinkTitle: "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0441\u0441\u044B\u043B\u043A\u0443",
5671
- removeLinkTitle: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0441\u044B\u043B\u043A\u0443"
5762
+ removeLinkTitle: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0441\u044B\u043B\u043A\u0443",
5763
+ openInNewTab: "\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0432 \u043D\u043E\u0432\u043E\u0439 \u0432\u043A\u043B\u0430\u0434\u043A\u0435",
5764
+ 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",
5765
+ addNofollow: '\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C rel="nofollow"',
5766
+ 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."
5672
5767
  },
5673
5768
  lists: {
5674
5769
  bulletList: "\u041D\u0435\u043D\u0443\u043C\u0435\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0441\u043F\u0438\u0441\u043E\u043A",
@@ -5828,7 +5923,11 @@ var en_default = {
5828
5923
  invalidUrl: "Invalid URL",
5829
5924
  emptyUrl: "Enter link URL",
5830
5925
  addLinkTitle: "Add link",
5831
- removeLinkTitle: "Remove link"
5926
+ removeLinkTitle: "Remove link",
5927
+ openInNewTab: "Open in new tab",
5928
+ openInNewTabHelp: "Link will open in a new browser window or tab",
5929
+ addNofollow: 'Add rel="nofollow"',
5930
+ addNofollowHelp: "Tells search engines not to count this link when calculating page rankings and indexing. Recommended for all external links."
5832
5931
  },
5833
5932
  lists: {
5834
5933
  bulletList: "Bullet list",
@@ -6024,6 +6123,7 @@ function TiptapContent({
6024
6123
  return false;
6025
6124
  }
6026
6125
  },
6126
+ onCreate: ({ editor: editor2 }) => handleChange?.(editor2.getJSON()),
6027
6127
  onBlur: ({ editor: editor2 }) => handleChange?.(editor2.getJSON())
6028
6128
  },
6029
6129
  [lang]
@@ -6048,8 +6148,8 @@ function TiptapContent({
6048
6148
  const handleVideoAdd = (url) => {
6049
6149
  createHandleVideoAdd(editor, handleCloseDialog, savedDialogCursorPos)(url);
6050
6150
  };
6051
- const handleLinkAdd = (title, url) => {
6052
- createHandleLinkAdd(editor, handleCloseDialog, savedDialogCursorPos)(title, url);
6151
+ const handleLinkAdd = (params) => {
6152
+ createHandleLinkAdd(editor, handleCloseDialog, params, savedDialogCursorPos);
6053
6153
  };
6054
6154
  const handleKatexAdd = (latex) => {
6055
6155
  const katexHandler = createHandleKatexAdd(editor, handleCloseDialog, savedDialogCursorPos);
@@ -6087,7 +6187,7 @@ function TiptapContent({
6087
6187
  BubbleMenuContent,
6088
6188
  {
6089
6189
  editor,
6090
- onLinkClick: (selectedText, url) => setLinkDialogContext({ selectedText, url })
6190
+ onLinkClick: (params) => setLinkDialogContext(params)
6091
6191
  }
6092
6192
  )
6093
6193
  }
@@ -6123,8 +6223,7 @@ function TiptapContent({
6123
6223
  open: true,
6124
6224
  onClose: handleCloseDialog,
6125
6225
  onLinkAdd: handleLinkAdd,
6126
- initialTitle: linkDialogContext?.selectedText || "",
6127
- initialUrl: linkDialogContext?.url || ""
6226
+ linkDialogContext
6128
6227
  }
6129
6228
  ),
6130
6229
  activeDialog === "video" && /* @__PURE__ */ jsx93(