@flozy/editor 5.5.1 → 5.5.2

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.
@@ -550,7 +550,10 @@ const CommonEditor = /*#__PURE__*/forwardRef((props, ref) => {
550
550
  renderLeaf: renderLeaf,
551
551
  decorate: d => decorators(d, editor),
552
552
  onKeyDown: onKeyDown,
553
- onSelect: () => handleCursorScroll(editorWrapper.current)
553
+ onSelect: () => handleCursorScroll(editorWrapper.current),
554
+ scrollSelectionIntoView: () => {
555
+ // disable the scrollSelectionIntoView, to resolve editor auto scroll on free-grid
556
+ }
554
557
  }), !readOnly ? /*#__PURE__*/_jsx(MentionsPopup, {
555
558
  ref: mentionsRef,
556
559
  mentions: mentions,
@@ -139,14 +139,17 @@ const MiniToolbar = props => {
139
139
  icon: Icon
140
140
  }) => {
141
141
  const isDisabled = popupType === type || type === "undo" ? !canUndo : type === "redo" ? !canRedo : false;
142
+ const isFocussed = editor?.selection?.anchor?.path;
143
+ const disableAddElement = type === "addElement" && !isFocussed;
142
144
  return /*#__PURE__*/_jsx(Tooltip, {
143
145
  arrow: true,
144
146
  title: label,
145
147
  disableHoverListener: toolTip,
146
148
  children: /*#__PURE__*/_jsx(IconButton, {
147
- className: `${type === popper ? "active" : ""} ${type === "undo" && !canUndo || type === "redo" && !canRedo ? "disabled" : ""} ${type === "undo" ? canUndo ? "activeUndo" : "disabledUndo" : type === "redo" ? canRedo ? "activeRedo" : "disabledRedo" : ""}`,
149
+ className: `${type === popper ? "active" : ""} ${type === "undo" && !canUndo || type === "redo" && !canRedo ? "disabled" : ""} ${type === "undo" ? canUndo ? "activeUndo" : "disabledUndo" : type === "redo" ? canRedo ? "activeRedo" : "disabledRedo" : ""} ${disableAddElement ? "disableAddElement" : ""}
150
+ `,
148
151
  onClick: handleClick(type),
149
- disabled: isDisabled,
152
+ disabled: isDisabled || disableAddElement,
150
153
  children: type === "page-settings" ? /*#__PURE__*/_jsx(PageSettingsButton, {
151
154
  from: "miniToolBar",
152
155
  icoBtnType: "mini",
@@ -79,6 +79,11 @@ const miniToolbarStyles = theme => ({
79
79
  stroke: theme?.palette?.editor?.svgStrokeDisabled
80
80
  }
81
81
  }
82
+ },
83
+ "&.disableAddElement": {
84
+ "& svg": {
85
+ stroke: theme?.palette?.editor?.svgStrokeDisabled
86
+ }
82
87
  }
83
88
  }
84
89
  }
@@ -1,4 +1,26 @@
1
1
  import { jsx } from "slate-hyperscript";
2
+ const inlineStyles = [{
3
+ key: "bold",
4
+ getStyle: styles => styles.fontWeight === "bold" || parseInt(styles.fontWeight, 10) >= 700
5
+ }, {
6
+ key: "italic",
7
+ getStyle: styles => styles.fontStyle === "italic"
8
+ }, {
9
+ key: "underline",
10
+ getStyle: styles => styles.textDecoration.includes("underline")
11
+ }];
12
+ function getInlineTextStyles(element) {
13
+ if (!element || !element.style) return {};
14
+ const styles = element.style;
15
+ const elementStyles = inlineStyles.reduce((total, currVal) => {
16
+ const style = currVal.getStyle(styles);
17
+ if (style) {
18
+ total[currVal.key] = style;
19
+ }
20
+ return total;
21
+ }, {});
22
+ return elementStyles;
23
+ }
2
24
  const handleTableCell = (el, children) => {
3
25
  const wrapChild = children?.map(c => {
4
26
  if (typeof c === "string") {
@@ -20,6 +42,24 @@ const handleTableCell = (el, children) => {
20
42
  }
21
43
  };
22
44
  };
45
+ const INLINE_TAGS = ["A", "ABBR", "B", "BDO", "CITE", "CODE", "DATA", "DEL", "DFN", "IMG", "INS", "KBD", "LABEL", "MARK", "Q", "SAMP", "SMALL", "SPAN", "SUB", "SUP", "TIME", "VAR"];
46
+
47
+ // to avoid nested paragraph to resolve performace issue
48
+ const paragraphType = el => {
49
+ const {
50
+ childNodes = []
51
+ } = el || {};
52
+
53
+ // if anyone of the child node is text node or wrapped with inline tags, it is considered to be an paragraph node
54
+ const isHavingText = childNodes?.length ? Array.from(childNodes)?.some(child => {
55
+ const isTextNode = child?.nodeType === 3;
56
+ const isHavingInlineTags = TEXT_TAGS[child?.nodeName] || INLINE_TAGS.includes(child.nodeName);
57
+ return isTextNode || isHavingInlineTags;
58
+ }) : null;
59
+ return isHavingText ? {
60
+ type: "paragraph"
61
+ } : {};
62
+ };
23
63
  const ELEMENT_TAGS = {
24
64
  A: el => ({
25
65
  type: "link",
@@ -59,24 +99,14 @@ const ELEMENT_TAGS = {
59
99
  OL: () => ({
60
100
  type: "orderedList"
61
101
  }),
62
- P: () => ({
63
- type: "paragraph"
64
- }),
65
- DIV: () => ({
66
- type: "paragraph"
67
- }),
102
+ P: paragraphType,
103
+ DIV: paragraphType,
68
104
  PRE: () => ({
69
105
  type: "code"
70
106
  }),
71
- META: () => ({
72
- type: "paragraph"
73
- }),
74
- STYLE: () => ({
75
- type: "paragraph"
76
- }),
77
- "GOOGLE-SHEETS-HTML-ORIGIN": () => ({
78
- type: "paragraph"
79
- }),
107
+ META: paragraphType,
108
+ STYLE: paragraphType,
109
+ "GOOGLE-SHEETS-HTML-ORIGIN": paragraphType,
80
110
  TABLE: (el, children = []) => {
81
111
  try {
82
112
  const bodyChild = children || [];
@@ -98,12 +128,8 @@ const ELEMENT_TAGS = {
98
128
  type: "table-row"
99
129
  }),
100
130
  TD: handleTableCell,
101
- COLGROUP: () => ({
102
- type: "paragraph"
103
- }),
104
- COL: () => ({
105
- type: "paragraph"
106
- })
131
+ COLGROUP: paragraphType,
132
+ COL: paragraphType
107
133
  };
108
134
 
109
135
  // COMPAT: `B` is omitted here because Google Docs uses `<b>` in weird ways.
@@ -129,11 +155,18 @@ const TEXT_TAGS = {
129
155
  U: () => ({
130
156
  underline: true
131
157
  })
158
+ // B: () => ({ bold: true }),
132
159
  };
160
+
133
161
  const deserialize = el => {
134
162
  if (el.nodeType === 3) {
163
+ // if there is any line-breaks
135
164
  const match = /\r|\n/.exec(el.textContent);
136
- return match ? null : el.textContent;
165
+ const text = el.textContent.replace(/\r|\n/g, "").trim();
166
+ return match && !text ? null : {
167
+ text,
168
+ ...getInlineTextStyles(el.parentNode)
169
+ };
137
170
  } else if (el.nodeType !== 1) {
138
171
  return null;
139
172
  } else if (el.nodeName === "BR") {
@@ -160,11 +193,20 @@ const deserialize = el => {
160
193
  overwriteChild,
161
194
  ...attrs
162
195
  } = ELEMENT_TAGS[nodeName](el, children);
163
- return jsx("element", attrs, overwriteChild || children);
196
+ if (attrs?.type) {
197
+ return jsx("element", attrs, overwriteChild || children);
198
+ }
164
199
  }
165
200
  if (TEXT_TAGS[nodeName]) {
166
201
  const attrs = TEXT_TAGS[nodeName](el);
167
- return children.map(child => jsx("text", attrs, child));
202
+ return children.map(child => {
203
+ if (child?.type) {
204
+ // if any list elements (like ul, ol... tags) is wrapped inside the TEXT_TAGS, we will return child as it is, else return it as text node
205
+ return child;
206
+ } else {
207
+ return jsx("text", attrs, child);
208
+ }
209
+ });
168
210
  }
169
211
  return children;
170
212
  };
@@ -4,6 +4,43 @@ import { decodeAndParseBase64 } from "../utils/helper";
4
4
  const avoidDefaultInsert = ["table", "grid"];
5
5
  const NON_TEXT_TAGS = ["ol", "ul", "img", "table", "video", "a", "button", "GOOGLE-SHEETS-HTML-ORIGIN"];
6
6
  const ALLOWED_TEXT_NODES = ["paragraph", "title", "headingOne", "headingTwo", "headingThree"];
7
+ const parseCopiedHTML = html => {
8
+ const parsed = new DOMParser().parseFromString(html, "text/html");
9
+
10
+ // if ol, ul are inside li, remove and push ol,ul after that li to maintain format between our slate list and external source list's json
11
+ parsed.querySelectorAll("li > ul, li > ol").forEach(list => {
12
+ // Find the parent li
13
+ const parentLi = list.parentElement;
14
+
15
+ // Move the list after the parent li
16
+ parentLi.after(list);
17
+ });
18
+
19
+ // to handle google docs list
20
+ parsed.querySelectorAll("li p, li div").forEach(element => {
21
+ const parent = element.parentNode;
22
+ // Move all child nodes of <p> or <div> to its parent <li>
23
+ while (element.firstChild) {
24
+ parent.insertBefore(element.firstChild, element);
25
+ }
26
+ // Remove the <p> or <div> element
27
+ parent.removeChild(element);
28
+ });
29
+
30
+ // claude.ai, copy list inbetween, some li tags are not wrapped with ul or ol
31
+ parsed.querySelectorAll("li").forEach(li => {
32
+ // Check if the parent of <li> is not a <ul> or <ol>
33
+ if (!li.parentElement || li.parentElement.tagName !== "UL" && li.parentElement.tagName !== "OL") {
34
+ // Create a <ul> element
35
+ const ul = document.createElement("ul");
36
+ // Append the <li> to the <ul>
37
+ ul.appendChild(li.cloneNode(true)); // Clone the <li>
38
+ // Replace the original <li> in the DOM with the <ul>
39
+ li.replaceWith(ul);
40
+ }
41
+ });
42
+ return parsed;
43
+ };
7
44
  const loopChildren = (children = [], defaultInsert) => {
8
45
  if (!children?.length) {
9
46
  return defaultInsert;
@@ -28,7 +65,7 @@ const getCurrentElement = editor => {
28
65
  return null;
29
66
  }
30
67
  };
31
- const getCurrentElementText = editor => {
68
+ export const getCurrentElementText = editor => {
32
69
  try {
33
70
  if (editor.selection) {
34
71
  return Editor.string(editor, editor?.selection?.anchor?.path);
@@ -186,15 +223,15 @@ const withHtml = editor => {
186
223
  return;
187
224
  }
188
225
  const currentText = getCurrentElementText(editor);
189
- if (currentText && !isTextNode) {
226
+ if (currentText?.trim() && !isTextNode) {
190
227
  insertAtNextNode(editor, decoded);
191
228
  return;
192
229
  }
193
230
  insertData(data);
194
231
  }
195
232
  } else if (html) {
196
- const parsed = new DOMParser().parseFromString(html, "text/html");
197
- const rootElement = parsed.body || parsed.documentElement;
233
+ const parsed = parseCopiedHTML(html);
234
+ const rootElement = parsed.body;
198
235
  const isNonText = rootElement ? rootElement?.querySelector(NON_TEXT_TAGS.toString()) : false;
199
236
  const isGoogleSheet = parsed.body.querySelector("google-sheets-html-origin");
200
237
  if (isGoogleSheet) {
@@ -230,7 +267,7 @@ const withHtml = editor => {
230
267
  return;
231
268
  }
232
269
  const currentText = getCurrentElementText(editor);
233
- if (currentText && isNonText) {
270
+ if (currentText?.trim() && isNonText) {
234
271
  insertAtNextNode(editor, formattedFragment);
235
272
  return;
236
273
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flozy/editor",
3
- "version": "5.5.1",
3
+ "version": "5.5.2",
4
4
  "description": "An Editor for flozy app brain",
5
5
  "files": [
6
6
  "dist"