@flozy/editor 3.7.0 → 3.7.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,361 @@
1
+ import { Editor, Transforms, Element as SlateElement } from "slate";
2
+ import { Box } from "@mui/material";
3
+ import { fontFamilyMap, sizeMap } from "../font";
4
+ import Table from "../../Elements/Table/Table";
5
+ import TableRow from "../../Elements/Table/TableRow";
6
+ import TableCell from "../../Elements/Table/TableCell";
7
+ import Mentions from "../../Elements/Mentions/Mentions";
8
+ import CheckList from "../../Elements/List/CheckList";
9
+ import { isEmptyTextNode } from "../../helper";
10
+ import { getBreakPointsValue } from "../../helper/theme";
11
+ import insertNewLine from "../insertNewLine";
12
+ import { getBorderColor } from "../helper";
13
+ import { jsx as _jsx } from "react/jsx-runtime";
14
+ const alignment = ["alignLeft", "alignRight", "alignCenter"];
15
+ const list_types = ["orderedList", "unorderedList"];
16
+ const LIST_FORMAT_TYPE = {
17
+ orderedList: "list-item",
18
+ unorderedList: "list-item"
19
+ };
20
+ const NEWLINESAFTER = ["headingOne", "headingTwo", "headingThree"];
21
+ export const toggleBlock = (editor, format, selection = true, attr = {}) => {
22
+ const isActive = isBlockActive(editor, format);
23
+ const isList = list_types.includes(format);
24
+ const isIndent = alignment.includes(format);
25
+ const isAligned = alignment.some(alignmentType => isBlockActive(editor, alignmentType));
26
+
27
+ /*If the node is already aligned and change in indent is called we should unwrap it first and split the node to prevent
28
+ messy, nested DOM structure and bugs due to that.*/
29
+ if (isAligned && isIndent) {
30
+ Transforms.unwrapNodes(editor, {
31
+ match: n => alignment.includes(!Editor.isEditor(n) && SlateElement.isElement(n) && n.type),
32
+ split: true
33
+ });
34
+ }
35
+
36
+ /* Wraping the nodes for alignment, to allow it to co-exist with other block level operations*/
37
+ if (isIndent) {
38
+ Transforms.wrapNodes(editor, {
39
+ type: format,
40
+ children: []
41
+ });
42
+ return;
43
+ }
44
+ Transforms.unwrapNodes(editor, {
45
+ match: n => list_types.includes(!Editor.isEditor(n) && SlateElement.isElement(n) && n.type),
46
+ split: true
47
+ });
48
+
49
+ // inserting blocks from "/"" commands remove searched word
50
+ if (!selection) {
51
+ Transforms.insertText(editor, "");
52
+ }
53
+ Transforms.setNodes(editor, {
54
+ type: isActive ? "paragraph" : isList ? LIST_FORMAT_TYPE[format] : format,
55
+ ...attr
56
+ });
57
+ if (isList && !isActive) {
58
+ Transforms.wrapNodes(editor, {
59
+ type: format,
60
+ children: []
61
+ });
62
+ }
63
+ if (NEWLINESAFTER.indexOf(format) > -1) {
64
+ insertNewLine(editor);
65
+ }
66
+ };
67
+ export const addMarkData = (editor, data) => {
68
+ try {
69
+ Editor.addMark(editor, data.format, data.value);
70
+ } catch (err) {
71
+ console.log(err);
72
+ }
73
+ };
74
+ export const toggleMark = (editor, format) => {
75
+ const isActive = isMarkActive(editor, format);
76
+ if (isActive) {
77
+ Editor.removeMark(editor, format);
78
+ } else {
79
+ Editor.addMark(editor, format, true);
80
+ }
81
+ };
82
+ export const isMarkActive = (editor, format) => {
83
+ try {
84
+ const marks = Editor.marks(editor);
85
+ return marks ? marks[format] === true : false;
86
+ } catch (err) {
87
+ console.log(err);
88
+ return null;
89
+ }
90
+ };
91
+ export const isBlockActive = (editor, format) => {
92
+ const [match] = Editor.nodes(editor, {
93
+ match: n => !Editor.isEditor(n) && SlateElement.isElement(n) && n.type === format
94
+ });
95
+ return !!match;
96
+ };
97
+ export const getBlockActive = (editor, format) => {
98
+ const [match] = Editor.nodes(editor, {
99
+ match: n => !Editor.isEditor(n) && SlateElement.isElement(n) && n.type === format
100
+ });
101
+ return {
102
+ isActive: !!match,
103
+ props: match && match[0]
104
+ };
105
+ };
106
+ export const upateBlockActive = (editor, format, attr = {}) => {
107
+ const [match] = Editor.nodes(editor, {
108
+ match: n => !Editor.isEditor(n) && SlateElement.isElement(n) && n.type === format
109
+ });
110
+ if (match && match[1] !== undefined) {
111
+ Transforms.setNodes(editor, attr, {
112
+ at: match[1]
113
+ });
114
+ }
115
+ };
116
+ export const activeMark = (editor, format) => {
117
+ const defaultMarkData = {
118
+ color: "#000000",
119
+ bgColor: "#FFFFFF",
120
+ fontSize: "normal",
121
+ fontFamily: "PoppinsRegular",
122
+ fontWeight: "normal"
123
+ };
124
+ try {
125
+ const marks = Editor.marks(editor);
126
+ const defaultValue = defaultMarkData[format];
127
+ return marks?.[format] ?? defaultValue;
128
+ } catch (err) {
129
+ console.log(err);
130
+ return null;
131
+ }
132
+ };
133
+ export const getMarked = (leaf, children) => {
134
+ const className = leaf?.doublequote ? "doublequote" : "";
135
+ if (leaf.bold) {
136
+ children = /*#__PURE__*/_jsx("strong", {
137
+ children: children
138
+ });
139
+ }
140
+ if (leaf.code) {
141
+ children = /*#__PURE__*/_jsx("code", {
142
+ children: children
143
+ });
144
+ }
145
+ if (leaf.italic) {
146
+ children = /*#__PURE__*/_jsx("em", {
147
+ children: children
148
+ });
149
+ }
150
+ if (leaf.strikethrough) {
151
+ children = /*#__PURE__*/_jsx("span", {
152
+ style: {
153
+ textDecoration: "line-through"
154
+ },
155
+ children: children
156
+ });
157
+ }
158
+ if (leaf.underline) {
159
+ children = /*#__PURE__*/_jsx("u", {
160
+ children: children
161
+ });
162
+ }
163
+ if (leaf.superscript) {
164
+ children = /*#__PURE__*/_jsx("sup", {
165
+ children: children
166
+ });
167
+ }
168
+ if (leaf.subscript) {
169
+ children = /*#__PURE__*/_jsx("sub", {
170
+ children: children
171
+ });
172
+ }
173
+ // cover under single span
174
+ if (leaf.color || leaf.bgColor || leaf.fontSize || leaf.fontFamily || leaf.fontWeight || className) {
175
+ const family = fontFamilyMap[leaf?.fontFamily];
176
+ const textStyles = leaf?.color?.indexOf("gradient") >= 0 ? {
177
+ background: leaf?.color?.concat("text"),
178
+ WebkitBackgroundClip: "text",
179
+ WebkitTextFillColor: "transparent"
180
+ } : {
181
+ color: leaf.color
182
+ };
183
+ children = /*#__PURE__*/_jsx("span", {
184
+ style: {
185
+ background: leaf.bgColor
186
+ },
187
+ children: /*#__PURE__*/_jsx(Box, {
188
+ className: className,
189
+ component: "span",
190
+ sx: {
191
+ fontSize: {
192
+ lg: sizeMap[leaf.fontSize] || leaf.fontSize,
193
+ ...getBreakPointsValue(leaf.fontSize, null, "overrideText")
194
+ },
195
+ ...textStyles,
196
+ fontFamily: family,
197
+ fontWeight: leaf.fontWeight
198
+ },
199
+ children: children
200
+ })
201
+ });
202
+ }
203
+ if (leaf.decoration === "link") {
204
+ children = /*#__PURE__*/_jsx("a", {
205
+ style: {
206
+ cursor: "pointer"
207
+ },
208
+ rel: "noreferrer",
209
+ target: "_blank",
210
+ href: leaf.text,
211
+ children: children
212
+ });
213
+ }
214
+ return children;
215
+ };
216
+ export const getBlock = props => {
217
+ const {
218
+ element,
219
+ children
220
+ } = props;
221
+ const attributes = props.attributes ?? {};
222
+ const isEmpty = isEmptyTextNode(element);
223
+ switch (element.type) {
224
+ case "paragraph":
225
+ return /*#__PURE__*/_jsx("p", {
226
+ ...attributes,
227
+ ...element.attr,
228
+ className: `content-editable ${isEmpty ? "empty" : ""}`
229
+ // placeholder="paragraph"
230
+ ,
231
+ children: children
232
+ });
233
+ case "headingOne":
234
+ return /*#__PURE__*/_jsx("h1", {
235
+ ...attributes,
236
+ ...element.attr,
237
+ className: `content-editable ${isEmpty ? "empty" : ""}`
238
+ // placeholder="Heading 1"
239
+ ,
240
+ children: children
241
+ });
242
+ case "headingTwo":
243
+ return /*#__PURE__*/_jsx("h2", {
244
+ ...attributes,
245
+ ...element.attr,
246
+ className: `content-editable ${isEmpty ? "empty" : ""}`
247
+ // placeholder="Heading 2"
248
+ ,
249
+ children: children
250
+ });
251
+ case "headingThree":
252
+ return /*#__PURE__*/_jsx("h3", {
253
+ ...attributes,
254
+ ...element.attr,
255
+ className: `content-editable ${isEmpty ? "empty" : ""}`
256
+ // placeholder="Heading 3"
257
+ ,
258
+ children: children
259
+ });
260
+ case "blockquote":
261
+ return /*#__PURE__*/_jsx("blockquote", {
262
+ ...attributes,
263
+ ...element.attr,
264
+ style: {
265
+ // borderColor: element?.color || "transparent",
266
+ ...getBorderColor(element?.color || "transparent", 3),
267
+ background: element?.bgColor || "none",
268
+ padding: `${element?.bgColor ? "16px" : "0px"} 8px`,
269
+ borderRadius: `${element?.color ? "0px" : "12px"} 12px 12px ${element?.color ? "0px" : "12px"}`,
270
+ margin: `${element?.bgColor ? "16px" : "0px"} 0px`,
271
+ width: element?.bgColor ? "calc(100% - 16px)" : "100%",
272
+ borderWidth: element?.color ? "0px 0px 0px 3px" : "0px"
273
+ },
274
+ children: children
275
+ });
276
+ case "alignLeft":
277
+ return /*#__PURE__*/_jsx("div", {
278
+ ...attributes,
279
+ ...element.attr,
280
+ children: children
281
+ });
282
+ case "alignCenter":
283
+ return /*#__PURE__*/_jsx("div", {
284
+ style: {
285
+ display: "flex",
286
+ alignItems: "center",
287
+ flexDirection: "column",
288
+ textAlign: "center"
289
+ },
290
+ ...attributes,
291
+ ...element.attr,
292
+ children: children
293
+ });
294
+ case "alignRight":
295
+ return /*#__PURE__*/_jsx("div", {
296
+ style: {
297
+ display: "flex",
298
+ alignItems: "flex-end",
299
+ flexDirection: "column",
300
+ textAlign: "right"
301
+ },
302
+ ...attributes,
303
+ ...element.attr,
304
+ children: children
305
+ });
306
+ case "list-item":
307
+ const firstChildren = element.children[0] || {};
308
+ return /*#__PURE__*/_jsx("li", {
309
+ ...attributes,
310
+ ...element.attr,
311
+ className: `content-editable ${isEmpty ? "empty" : ""}`,
312
+ placeholder: "List",
313
+ style: {
314
+ color: firstChildren?.color
315
+ },
316
+ children: children
317
+ });
318
+ case "orderedList":
319
+ return /*#__PURE__*/_jsx("ol", {
320
+ type: "1",
321
+ ...attributes,
322
+ children: children
323
+ });
324
+ case "unorderedList":
325
+ return /*#__PURE__*/_jsx("ul", {
326
+ ...attributes,
327
+ children: children
328
+ });
329
+ case "check-list-item":
330
+ return /*#__PURE__*/_jsx(CheckList, {
331
+ ...props,
332
+ isEmpty: isEmpty
333
+ });
334
+ case "table":
335
+ return /*#__PURE__*/_jsx(Table, {
336
+ ...props
337
+ });
338
+ case "table-head":
339
+ return /*#__PURE__*/_jsx("thead", {
340
+ children: children
341
+ });
342
+ case "table-body":
343
+ return /*#__PURE__*/_jsx("tbody", {
344
+ children: children
345
+ });
346
+ case "table-row":
347
+ return /*#__PURE__*/_jsx(TableRow, {
348
+ ...props
349
+ });
350
+ case "table-cell":
351
+ return /*#__PURE__*/_jsx(TableCell, {
352
+ ...props
353
+ });
354
+ case "mention":
355
+ return /*#__PURE__*/_jsx(Mentions, {
356
+ ...props
357
+ });
358
+ default:
359
+ return;
360
+ }
361
+ };
@@ -208,7 +208,13 @@ export const hasVerticalScrollbar = (element = {}) => {
208
208
  return element.scrollHeight > element.clientHeight;
209
209
  };
210
210
  const isHomePage = page => {
211
- return page === "home" || page === "iframe.html" || !page;
211
+ return page === "home" || page === "iframe.html" || page === "_currentPage" || !page;
212
+ };
213
+ const getScrollElement = () => {
214
+ const slateWrapper = document.getElementById("slate-wrapper-scroll-container");
215
+ const isSlateWrapperScroll = hasVerticalScrollbar(slateWrapper);
216
+ const scrollFrom = isSlateWrapperScroll ? slateWrapper : window;
217
+ return scrollFrom;
212
218
  };
213
219
  export const handleLinkType = (url, linkType, readOnly, openInNewTab, onClick = () => {}) => {
214
220
  const props = {};
@@ -239,16 +245,33 @@ export const handleLinkType = (url, linkType, readOnly, openInNewTab, onClick =
239
245
  }
240
246
  break;
241
247
  case "page":
242
- props.component = "a";
243
- const [page, section] = url?.split("#") || [];
248
+ const [page = "", section] = url?.split("#") || [];
244
249
  const sec = section ? `#${section}` : "";
245
- const currentUserPage = getCurrentUserPage();
246
- props.href = isHomePage(page) ? `./${currentUserPage}${sec}` : `./${url}`;
247
- if (openInNewTab) {
248
- if (isCurrentPage(page)) {
249
- // temp fix, if user is presented in current page, open in new tab option is restricted, we will scroll to the element in current page
250
- } else {
251
- props.target = "_blank";
250
+ if (page === "_currentPage") {
251
+ props.component = "button";
252
+ props.onClick = () => {
253
+ const scrollFrom = getScrollElement();
254
+ if (sec) {
255
+ const element = document.getElementById(section);
256
+ if (element) {
257
+ const topPosition = element.getBoundingClientRect().top + scrollFrom.scrollTop;
258
+ scrollFrom.scrollTo({
259
+ top: topPosition,
260
+ behavior: "smooth"
261
+ });
262
+ }
263
+ }
264
+ };
265
+ } else {
266
+ props.component = "a";
267
+ const currentUserPage = getCurrentUserPage();
268
+ props.href = isCurrentPage(page) ? `./${currentUserPage}${sec}` : `./${url}`;
269
+ if (openInNewTab) {
270
+ if (isCurrentPage(page)) {
271
+ // temp fix, if user is presented in current page, open in new tab option is restricted, we will scroll to the element in current page
272
+ } else {
273
+ props.target = "_blank";
274
+ }
252
275
  }
253
276
  }
254
277
  break;
@@ -1,22 +1,34 @@
1
1
  import { renderToString } from "react-dom/server";
2
- import { getBlock } from "./SlateUtilityFunctions";
2
+ import { getBlock } from "./chatEditor/SlateUtilityFunctions";
3
3
  const serializeToHTML = nodes => {
4
4
  try {
5
- const htmlString = nodes.map(n => {
6
- if (n) {
5
+ const htmlString = nodes.map(node => {
6
+ // Render function for each node
7
+ const renderNode = node => {
7
8
  const props = {
8
- element: n,
9
- children: n?.children
9
+ element: node,
10
+ children: node.children?.map(child => {
11
+ // Render each child node
12
+ if (child.text !== undefined) {
13
+ // For text nodes, return the text
14
+ return child.text;
15
+ }
16
+ // For other nodes, render them recursively
17
+ return renderNode(child);
18
+ }) || []
10
19
  };
11
- const reactString = renderToString(getBlock(props));
12
- return reactString;
13
- } else {
14
- return "";
15
- }
16
- });
17
- console.log(htmlString);
20
+ return getBlock(props);
21
+ };
22
+
23
+ // Render root node to HTML string
24
+ const reactElement = renderNode(node);
25
+ return renderToString(reactElement);
26
+ }).join(''); // Combine all HTML strings
27
+
28
+ return htmlString;
18
29
  } catch (err) {
19
- console.log(err);
30
+ console.error('Error serializing to HTML:', err);
31
+ return '';
20
32
  }
21
33
  };
22
34
  export default serializeToHTML;
package/dist/index.js CHANGED
@@ -2,7 +2,11 @@ import Collaborative from "./Editor/CollaborativeEditor";
2
2
  import CommonEditor from "./Editor/CommonEditor";
3
3
  import Mini from "./Editor/MiniEditor";
4
4
  import EditorInFrame from "./Editor/IframeEditor";
5
+ import Chat from "./Editor/ChatEditor";
6
+ import Emoji from "./Editor/Elements/Emoji/EmojiPicker";
5
7
  export const Editor = CommonEditor;
6
8
  export const MiniEditor = Mini;
7
9
  export const CollaborativeEditor = Collaborative;
8
- export const IframeEditor = EditorInFrame;
10
+ export const IframeEditor = EditorInFrame;
11
+ export const ChatEditor = Chat;
12
+ export const EmojiPicker = Emoji;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flozy/editor",
3
- "version": "3.7.0",
3
+ "version": "3.7.2",
4
4
  "description": "An Editor for flozy app brain",
5
5
  "files": [
6
6
  "dist"
@@ -58,11 +58,11 @@
58
58
  "prepare": "husky install .husky",
59
59
  "lint": "./node_modules/.bin/eslint --ignore-path .gitignore .",
60
60
  "start": "craco start",
61
- "build": "craco build",
61
+ "build": "NODE_OPTIONS='--max_old_space_size=4096' craco build",
62
62
  "test": "craco test --passWithNoTests",
63
63
  "eject": "react-scripts eject",
64
- "storybook": "storybook dev -p 6006",
65
- "build-storybook": "storybook build",
64
+ "storybook": "NODE_OPTIONS='--max_old_space_size=4096' storybook dev -p 6006",
65
+ "build-storybook": "NODE_OPTIONS='--max_old_space_size=4096' storybook build",
66
66
  "publish:npm": "rm -rf dist && mkdir dist && babel src/components -d dist --copy-files",
67
67
  "publish:local": "rm -rf /Users/agenciflow08/Documents/flozy/client/node_modules/@flozy/editor/dist && babel src/components -d /Users/agenciflow08/Documents/flozy/client/node_modules/@flozy/editor/dist --copy-files"
68
68
  },