@cyberyozh/text-editor 0.1.5 → 0.1.7

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 (34) hide show
  1. package/dist/editor.css +46 -20
  2. package/dist/editor.css.map +1 -1
  3. package/dist/editor.js +505 -430
  4. package/dist/editor.js.map +1 -1
  5. package/dist/editor.mjs +506 -431
  6. package/dist/editor.mjs.map +1 -1
  7. package/dist/render.js +62 -29
  8. package/dist/render.js.map +1 -1
  9. package/dist/render.mjs +62 -29
  10. package/dist/render.mjs.map +1 -1
  11. package/dist/styles/index.css +46 -20
  12. package/package.json +5 -1
  13. package/src/editor/commands/items.ts +1 -1
  14. package/src/editor/components/common-modal/common-modal.css +2 -1
  15. package/src/editor/context/dialog-command.tsx +0 -2
  16. package/src/editor/controls/bubble-menu-content.tsx +6 -2
  17. package/src/editor/controls/commands-list.tsx +7 -0
  18. package/src/editor/dialogs/add-video-dialog/add-video-dialog.tsx +2 -5
  19. package/src/editor/editor.tsx +69 -44
  20. package/src/editor/handlers/dialog-handlers.ts +118 -36
  21. package/src/extensions/views/components/index.ts +1 -0
  22. package/src/extensions/views/components/table.tsx +29 -0
  23. package/src/render/static-renderer-config.tsx +32 -2
  24. package/src/styles/editor/base.css +7 -5
  25. package/src/styles/editor/states.css +4 -3
  26. package/src/styles/editor/typography.css +10 -2
  27. package/src/styles/editor/variables.css +1 -0
  28. package/src/styles/extensions/callout.css +5 -2
  29. package/src/styles/extensions/code-block.css +2 -1
  30. package/src/styles/extensions/commands-list.css +5 -1
  31. package/src/styles/extensions/custom-quote.css +5 -0
  32. package/src/styles/extensions/details.css +4 -4
  33. package/src/styles/extensions/myth-reality.css +3 -1
  34. package/src/styles/extensions/prompt.css +4 -1
@@ -81,7 +81,7 @@
81
81
  /* src/editor/components/common-modal/common-modal.css */
82
82
  .modal-overlay {
83
83
  display: flex;
84
- z-index: 10000;
84
+ z-index: 10001;
85
85
  position: fixed;
86
86
  top: 0;
87
87
  right: 0;
@@ -102,6 +102,7 @@
102
102
  }
103
103
  }
104
104
  .modal-content {
105
+ z-index: 10002;
105
106
  position: relative;
106
107
  width: 100%;
107
108
  max-width: 500px;
@@ -879,6 +880,10 @@
879
880
  border: none;
880
881
  outline: none;
881
882
  background: transparent;
883
+ font-weight: 500;
884
+ font-size: 18px;
885
+ line-height: 140%;
886
+ letter-spacing: -0.36px;
882
887
  list-style: none;
883
888
  cursor: pointer;
884
889
  transition: all 150ms ease-in-out;
@@ -948,11 +953,13 @@
948
953
  }
949
954
  .myth-reality-block-content p:first-child {
950
955
  margin-bottom: 8px;
951
- font-weight: 600;
956
+ font-weight: 600 !important;
952
957
  }
958
+ .myth-reality-block-content p,
953
959
  .myth-reality-block-content ul p,
954
960
  .myth-reality-block-content li p {
955
961
  margin-bottom: 0;
962
+ font-weight: 400;
956
963
  font-size: var(--tiptap-font-size);
957
964
  }
958
965
 
@@ -985,6 +992,11 @@
985
992
  flex-direction: column;
986
993
  max-width: 700px;
987
994
  margin: 0 auto 16px;
995
+ font-style: normal;
996
+ font-weight: 500;
997
+ font-size: 18px;
998
+ line-height: 140%;
999
+ letter-spacing: -0.36px;
988
1000
  }
989
1001
  [data-custom-quote-text] .is-empty::before {
990
1002
  position: absolute;
@@ -1070,18 +1082,12 @@
1070
1082
  }
1071
1083
  .callout-node {
1072
1084
  display: flex;
1085
+ align-items: baseline;
1073
1086
  margin: 32px 0;
1074
1087
  padding: 16px;
1075
1088
  gap: 10px;
1076
1089
  border-radius: 8px;
1077
1090
  }
1078
- .callout-node:has(.callout-content h2),
1079
- .callout-node:has(.callout-content h3),
1080
- .callout-node:has(.callout-content h4),
1081
- .callout-node:has(.callout-content h5),
1082
- .callout-node:has(.callout-content h6) {
1083
- align-items: baseline;
1084
- }
1085
1091
  .callout-node p {
1086
1092
  font-size: 16px;
1087
1093
  line-height: 22px;
@@ -1134,6 +1140,8 @@
1134
1140
  .callout-content-wrapper {
1135
1141
  flex: 1;
1136
1142
  min-width: 0;
1143
+ font-size: var(--tiptap-font-size);
1144
+ line-height: 1;
1137
1145
  }
1138
1146
  .callout-content {
1139
1147
  color: inherit;
@@ -1226,6 +1234,7 @@
1226
1234
  flex: 1;
1227
1235
  max-width: 50%;
1228
1236
  color: var(--tiptap-code-block-text);
1237
+ font-size: 14px;
1229
1238
  font-family: var(--tiptap-code-block-font-family);
1230
1239
  }
1231
1240
  .tiptap-code-block-copy-button {
@@ -1305,8 +1314,11 @@
1305
1314
  justify-content: space-between;
1306
1315
  margin-bottom: 16px;
1307
1316
  gap: 8px;
1317
+ font-style: normal;
1308
1318
  font-weight: 600;
1309
- font-size: var(--tiptap-prompt-header-font-size);
1319
+ font-size: var(--tiptap-font-size);
1320
+ line-height: 140%;
1321
+ letter-spacing: -0.36px;
1310
1322
  }
1311
1323
  .prompt-content-wrapper {
1312
1324
  display: flex;
@@ -1416,9 +1428,12 @@
1416
1428
  }
1417
1429
 
1418
1430
  /* src/styles/extensions/commands-list.css */
1431
+ .react-renderer:has(.commands-list-container) {
1432
+ z-index: 10000;
1433
+ }
1419
1434
  .commands-list-container {
1420
1435
  display: flex;
1421
- z-index: 50;
1436
+ position: relative;
1422
1437
  flex-direction: column;
1423
1438
  width: 280px;
1424
1439
  max-height: 300px;
@@ -1500,6 +1515,7 @@
1500
1515
  --tiptap-color-link: #0059df;
1501
1516
  --tiptap-color-primary-error: #f04438;
1502
1517
  --tiptap-color-border: #f2f4f7;
1518
+ --tiptap-color-bg-disabled: #f7f7f8;
1503
1519
  --tiptap-color-divider: #aaabb7;
1504
1520
  --tiptap-color-text-disabled: #aaabb7;
1505
1521
  --tiptap-color-text-placeholder: #aaabb7;
@@ -1546,19 +1562,21 @@
1546
1562
  .tiptap-content-wrapper {
1547
1563
  position: relative;
1548
1564
  margin: 0 !important;
1549
- }
1550
- .tiptap {
1551
1565
  padding: var(--tiptap-spacing);
1552
1566
  border: 1px solid var(--tiptap-color-border);
1553
1567
  border-radius: var(--tiptap-radius);
1554
1568
  font-size: var(--tiptap-font-size);
1555
1569
  font-family: var(--tiptap-font-family);
1556
1570
  }
1557
- .tiptap:focus {
1571
+ .tiptap-content-wrapper:focus {
1558
1572
  border-color: var(--tiptap-color-primary-light);
1559
1573
  outline: none;
1560
1574
  }
1561
- .tiptap-content-wrapper.has-error .tiptap {
1575
+ .tiptap-content-wrapper .ProseMirror {
1576
+ border: none;
1577
+ outline: none;
1578
+ }
1579
+ .tiptap-content-wrapper.has-error {
1562
1580
  border-color: var(--tiptap-color-primary-error);
1563
1581
  }
1564
1582
  .tiptap-error-message {
@@ -1583,10 +1601,10 @@
1583
1601
  font-weight: 400;
1584
1602
  font-size: var(--tiptap-font-size);
1585
1603
  line-height: 140%;
1586
- letter-spacing: 0;
1604
+ letter-spacing: normal;
1587
1605
  }
1588
1606
  .tiptap > * {
1589
- letter-spacing: 0;
1607
+ letter-spacing: normal;
1590
1608
  }
1591
1609
  .tiptap p:not(:last-child),
1592
1610
  .tiptap ul:not(:last-child),
@@ -1594,6 +1612,12 @@
1594
1612
  .tiptap blockquote:not(:last-child) {
1595
1613
  margin-bottom: var(--tiptap-spacing);
1596
1614
  }
1615
+ .tiptap h1 {
1616
+ font-weight: 600;
1617
+ font-size: 40px;
1618
+ line-height: 110%;
1619
+ letter-spacing: 0;
1620
+ }
1597
1621
  .tiptap h2 {
1598
1622
  font-weight: 600;
1599
1623
  font-size: 36px;
@@ -1645,6 +1669,7 @@
1645
1669
  .tiptap ul,
1646
1670
  .tiptap ol {
1647
1671
  padding-left: 32px;
1672
+ font-size: var(--tiptap-font-size);
1648
1673
  }
1649
1674
  .tiptap ul:not(:last-child),
1650
1675
  .tiptap ol:not(:last-child),
@@ -1890,12 +1915,13 @@
1890
1915
  background-color: transparent;
1891
1916
  }
1892
1917
  .tiptap-content-wrapper.is-disabled {
1893
- opacity: 0.6;
1918
+ padding: var(--tiptap-spacing);
1894
1919
  cursor: not-allowed;
1920
+ opacity: 0.6;
1895
1921
  pointer-events: none;
1896
1922
  }
1897
- .tiptap-content-wrapper.is-disabled .ProseMirror {
1898
- background-color: var(--tiptap-color-grey-1, #f5f5f5);
1923
+ .tiptap-content-wrapper.is-disabled {
1924
+ background-color: var(--tiptap-color-bg-disabled);
1899
1925
  }
1900
1926
 
1901
1927
  /* src/styles/editor/callout-menu.css */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyberyozh/text-editor",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Rich text editor library based on Tiptap for CyberYozh",
5
5
  "author": "CyberYozh",
6
6
  "license": "MIT",
@@ -42,6 +42,10 @@
42
42
  "release": "npm run build && changeset publish",
43
43
  "release:complete": "git add . && git commit -m \"chore: release v$(node -p \"require('./package.json').version\")\" && npm run release && git push --follow-tags"
44
44
  },
45
+ "peerDependencies": {
46
+ "react": "^18.0.0 || ^19.0.0",
47
+ "react-dom": "^18.0.0 || ^19.0.0"
48
+ },
45
49
  "dependencies": {
46
50
  "@floating-ui/dom": "^1.7.4",
47
51
  "@tiptap/core": "^3.10.6",
@@ -187,7 +187,7 @@ export const getCommandItems = (
187
187
  .focus()
188
188
  .insertContent({
189
189
  type: "callout",
190
- attrs: { type: "", bgColor: "#F7F7F8" },
190
+ attrs: { type: "", bgColor: "#F7F7F8", emoji: "callout-info" },
191
191
  content: [
192
192
  {
193
193
  type: "paragraph",
@@ -1,6 +1,6 @@
1
1
  .modal-overlay {
2
2
  display: flex;
3
- z-index: 10000;
3
+ z-index: 10001;
4
4
  position: fixed;
5
5
  top: 0;
6
6
  right: 0;
@@ -23,6 +23,7 @@
23
23
  }
24
24
 
25
25
  .modal-content {
26
+ z-index: 10002;
26
27
  position: relative;
27
28
  width: 100%;
28
29
  max-width: 500px;
@@ -2,8 +2,6 @@
2
2
 
3
3
  import { createContext, useContext, useState, ReactNode } from "react";
4
4
 
5
- import type { Editor } from "@tiptap/react";
6
-
7
5
  export type DialogCommandType = "image" | "video" | "link" | "katex";
8
6
 
9
7
  interface DialogCommandContextType {
@@ -68,7 +68,6 @@ export const BubbleMenuContent = ({ editor, onLinkClick }: Props) => {
68
68
  } = useEditorState({
69
69
  editor,
70
70
  selector: (ctx) => {
71
- if (!editor) return {};
72
71
  return {
73
72
  canUndo: ctx.editor.can().undo(),
74
73
  canRedo: ctx.editor.can().redo(),
@@ -113,7 +112,12 @@ export const BubbleMenuContent = ({ editor, onLinkClick }: Props) => {
113
112
  };
114
113
 
115
114
  return (
116
- <div className={`bubble-menu ${textColor || highlightColor ? "hide-text-selection" : ""}`}>
115
+ <div
116
+ className={`bubble-menu ${textColor || highlightColor ? "hide-text-selection" : ""}`}
117
+ onMouseDown={(e) => {
118
+ e.preventDefault();
119
+ }}
120
+ >
117
121
  <div className="tiptap-controls-group">
118
122
  <button
119
123
  className="tiptap-control-button"
@@ -78,7 +78,10 @@ export const CommandsList = forwardRef<
78
78
  if (item) {
79
79
  if (item.dialogCommand) {
80
80
  openDialogCommand(item.dialogCommand);
81
+ // Also call command to ensure menu closes
82
+ command(item);
81
83
  } else {
84
+ console.log("open video");
82
85
  command(item);
83
86
  }
84
87
  }
@@ -98,6 +101,10 @@ export const CommandsList = forwardRef<
98
101
  <div
99
102
  ref={listRef}
100
103
  className="commands-list-container"
104
+ onMouseDown={(e) => {
105
+ e.preventDefault();
106
+ e.stopPropagation();
107
+ }}
101
108
  >
102
109
  {items.map((item, index) => (
103
110
  <button
@@ -19,11 +19,7 @@ const SUPPORTED_PLATFORMS = {
19
19
  [VideoHosting.VK]: "VK",
20
20
  };
21
21
 
22
- export const AddVideoDialog = ({
23
- open,
24
- onClose,
25
- onVideoAdd,
26
- }: AddVideoDialogProps) => {
22
+ export const AddVideoDialog = ({ open, onClose, onVideoAdd }: AddVideoDialogProps) => {
27
23
  const translations = useTiptapTranslations();
28
24
  const [url, setUrl] = useState("");
29
25
  const [error, setError] = useState<string>("");
@@ -112,6 +108,7 @@ export const AddVideoDialog = ({
112
108
  <input
113
109
  ref={urlInputRef}
114
110
  id="video-url"
111
+ autoComplete="off"
115
112
  type="text"
116
113
  className="video-dialog-input"
117
114
  placeholder={translations.videoUpload.urlPlaceholder}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { useState } from "react";
3
+ import { useState, useEffect } from "react";
4
4
 
5
5
  import { EditorState, NodeSelection } from "@tiptap/pm/state";
6
6
  import { Slice } from "@tiptap/pm/model";
@@ -87,6 +87,7 @@ function TiptapContent({
87
87
  attrs: FigureAttrs;
88
88
  pos?: number;
89
89
  } | null>(null);
90
+ const [savedDialogCursorPos, setSavedDialogCursorPos] = useState<number | undefined>(undefined);
90
91
  const { activeDialog, closeDialogCommand, openDialogCommand } = useDialogCommand();
91
92
  const translations = useTiptapTranslations();
92
93
 
@@ -141,23 +142,37 @@ function TiptapContent({
141
142
  [lang]
142
143
  );
143
144
 
145
+ useEffect(() => {
146
+ if (activeDialog && editor && !editor.isDestroyed) {
147
+ const pos = editor.state.selection.from;
148
+ setSavedDialogCursorPos(pos);
149
+ }
150
+ }, [activeDialog, editor]);
151
+
144
152
  const handleCloseDialog = () => {
145
153
  setLinkDialogContext(null);
146
154
  setKatexDialogContext(null);
147
155
  setEditFigureContext(null);
156
+ setSavedDialogCursorPos(undefined);
148
157
  closeDialogCommand();
149
158
  };
150
159
 
151
- const handleImageAdd = createHandleImageAdd(editor, handleCloseDialog);
160
+ const handleImageAdd = createHandleImageAdd(editor, handleCloseDialog, savedDialogCursorPos);
152
161
  const handleImageEdit = createHandleImageEdit(editor, handleCloseDialog, editFigureContext?.pos);
153
- const handleVideoAdd = createHandleVideoAdd(editor, handleCloseDialog);
154
- const handleLinkAdd = createHandleLinkAdd(editor, handleCloseDialog);
162
+ const handleVideoAdd = createHandleVideoAdd(editor, handleCloseDialog, savedDialogCursorPos);
163
+ const handleLinkAdd = createHandleLinkAdd(editor, handleCloseDialog, savedDialogCursorPos);
155
164
  const handleKatexAdd = (latex: string) => {
156
- const katexHandler = createHandleKatexAdd(editor, handleCloseDialog);
165
+ const katexHandler = createHandleKatexAdd(editor, handleCloseDialog, savedDialogCursorPos);
157
166
  katexHandler(latex, katexDialogContext?.pos);
158
167
  };
159
168
 
160
- if (!editor) return null;
169
+ useEffect(() => {
170
+ if (!editor || editor.isDestroyed) return;
171
+
172
+ editor.commands.setContent(safeContent, {
173
+ emitUpdate: false,
174
+ });
175
+ }, [content, editor]);
161
176
 
162
177
  const shouldBubbleMenuShow = ({ state }: { state: EditorState }) => {
163
178
  if (state.selection instanceof NodeSelection) {
@@ -175,11 +190,13 @@ function TiptapContent({
175
190
  return true;
176
191
  };
177
192
 
193
+ if (!editor) return null;
194
+
178
195
  return (
179
196
  <>
180
- {!isDisabled && <DragHandle editor={editor} />}
197
+ {<DragHandle editor={editor} />}
181
198
  {isEditable && !isDisabled && <TableMenu editor={editor} />}
182
- {!isDisabled && (
199
+ {isEditable && !isDisabled && (
183
200
  <BubbleMenu
184
201
  shouldShow={shouldBubbleMenuShow}
185
202
  editor={editor}
@@ -192,50 +209,58 @@ function TiptapContent({
192
209
  </BubbleMenu>
193
210
  )}
194
211
 
195
- <div className={`tiptap-content-wrapper ${!!error ? "has-error" : ""} ${isDisabled ? "is-disabled" : ""}`}>
212
+ <div
213
+ className={`tiptap-content-wrapper ${!!error ? "has-error" : ""} ${
214
+ isDisabled ? "is-disabled" : ""
215
+ }`}
216
+ >
196
217
  <EditorContent editor={editor} />
197
218
  {isEditable && !isDisabled && <CharacterCountComponent editor={editor} />}
198
219
  </div>
199
220
  {!!error && <p className="tiptap-error-message">{error}</p>}
200
221
 
201
- {activeDialog === "image" && isEditable && (
202
- <ImageDialog
203
- open={true}
204
- onClose={handleCloseDialog}
205
- onImageAdd={handleImageAdd}
206
- onImageEdit={handleImageEdit}
207
- uploadHandler={imageHandlers?.upload}
208
- getImageById={imageHandlers?.getImage}
209
- initialAttrs={editFigureContext?.attrs || undefined}
210
- mode={editFigureContext ? "edit" : "add"}
211
- />
212
- )}
222
+ {isEditable && (
223
+ <>
224
+ {activeDialog === "image" && (
225
+ <ImageDialog
226
+ open={true}
227
+ onClose={handleCloseDialog}
228
+ onImageAdd={handleImageAdd}
229
+ onImageEdit={handleImageEdit}
230
+ uploadHandler={imageHandlers?.upload}
231
+ getImageById={imageHandlers?.getImage}
232
+ initialAttrs={editFigureContext?.attrs || undefined}
233
+ mode={editFigureContext ? "edit" : "add"}
234
+ />
235
+ )}
213
236
 
214
- {activeDialog === "link" && (
215
- <AddLinkDialog
216
- open={true}
217
- onClose={handleCloseDialog}
218
- onLinkAdd={handleLinkAdd}
219
- initialTitle={linkDialogContext?.selectedText || ""}
220
- initialUrl={linkDialogContext?.url || ""}
221
- />
222
- )}
237
+ {activeDialog === "link" && (
238
+ <AddLinkDialog
239
+ open={true}
240
+ onClose={handleCloseDialog}
241
+ onLinkAdd={handleLinkAdd}
242
+ initialTitle={linkDialogContext?.selectedText || ""}
243
+ initialUrl={linkDialogContext?.url || ""}
244
+ />
245
+ )}
223
246
 
224
- {activeDialog === "video" && (
225
- <AddVideoDialog
226
- open={true}
227
- onClose={handleCloseDialog}
228
- onVideoAdd={handleVideoAdd}
229
- />
230
- )}
247
+ {activeDialog === "video" && (
248
+ <AddVideoDialog
249
+ open={true}
250
+ onClose={handleCloseDialog}
251
+ onVideoAdd={handleVideoAdd}
252
+ />
253
+ )}
231
254
 
232
- {activeDialog === "katex" && (
233
- <AddKatexDialog
234
- open={true}
235
- onClose={handleCloseDialog}
236
- onKatexAdd={handleKatexAdd}
237
- initialContent={katexDialogContext?.latex || ""}
238
- />
255
+ {activeDialog === "katex" && (
256
+ <AddKatexDialog
257
+ open={true}
258
+ onClose={handleCloseDialog}
259
+ onKatexAdd={handleKatexAdd}
260
+ initialContent={katexDialogContext?.latex || ""}
261
+ />
262
+ )}
263
+ </>
239
264
  )}
240
265
  </>
241
266
  );