@flozy/editor 1.2.2 → 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  import React, { useRef, useCallback, useEffect, useMemo, useState, forwardRef, useImperativeHandle } from "react";
2
2
  import { createEditor } from "slate";
3
- import { Slate, Editable } from "slate-react";
4
- import html2canvas from "html2canvas";
3
+ import { Slate, Editable, ReactEditor } from "slate-react";
4
+ import { useDebounce } from "use-debounce";
5
5
  import Toolbar from "./Toolbar/Toolbar";
6
6
  import { getMarked, getBlock } from "./utils/SlateUtilityFunctions";
7
7
  import CodeToText from "./Elements/CodeToText/CodeToText";
@@ -12,11 +12,13 @@ import { RemoteCursorOverlay } from "./RemoteCursorOverlay/Overlay";
12
12
  import { mentionsEvent, commands } from "./utils/events";
13
13
  import withCommon from "./hooks/withCommon";
14
14
  import DialogWrapper from "./DialogWrapper";
15
- import useTimeout from "./hooks/useTimeout";
16
15
  import "./Editor.css";
17
16
  import { serialize } from "./utils/serializer";
17
+ import { getThumbnailImage } from "./helper";
18
+ import PopupTool from "./Toolbar/PopupTool";
18
19
  import { jsx as _jsx } from "react/jsx-runtime";
19
20
  import { jsxs as _jsxs } from "react/jsx-runtime";
21
+ const PREVIEW_IMAGE_HIDE_CLASS = ["grid-container-toolbar", "grid-item-toolbar", "element-toolbar"];
20
22
  const Element = props => {
21
23
  return getBlock(props);
22
24
  };
@@ -38,18 +40,16 @@ const CommonEditor = /*#__PURE__*/forwardRef((props, ref) => {
38
40
  onSave,
39
41
  editor: collaborativeEditor,
40
42
  readOnly,
41
- otherProps,
42
- timeoutInMS = 1000
43
+ otherProps
43
44
  } = props;
44
45
  const editorWrapper = useRef();
45
46
  const convertedContent = draftToSlate({
46
47
  data: content
47
48
  });
48
49
  const [value, setValue] = useState(convertedContent);
49
- const [lastUpdated, setLastUpdated] = useState(JSON.stringify(convertedContent));
50
- const [count] = useTimeout({
51
- timeoutInMS: timeoutInMS
52
- });
50
+ const [loadedValue] = useState(value);
51
+ const [isInteracted, setIsInteracted] = useState(false);
52
+ const [deboundedValue] = useDebounce(value, 500);
53
53
  const editor = useMemo(() => {
54
54
  if (collaborativeEditor) return collaborativeEditor;
55
55
  return withCommon(createEditor());
@@ -74,17 +74,41 @@ const CommonEditor = /*#__PURE__*/forwardRef((props, ref) => {
74
74
  }));
75
75
  }, [id, content]);
76
76
  useEffect(() => {
77
- if (count > 0) {
78
- updateChanges(value);
77
+ if (editorWrapper && editorWrapper?.current && loadedValue !== deboundedValue && isInteracted) {
78
+ const text = serialize(deboundedValue);
79
+ const title = deboundedValue?.find(f => f.type === "title");
80
+ onSave(JSON.stringify(deboundedValue), {
81
+ text: text,
82
+ title: serialize(title?.children) || "Untitled"
83
+ });
79
84
  }
80
- }, [count]);
85
+ }, [deboundedValue]);
86
+ const getPreviewImage = async (needBackground = false, options = {}) => {
87
+ ReactEditor.blur(editor);
88
+ const dom = needBackground ? editorWrapper?.current : editorWrapper?.current.getElementsByClassName("innert-editor-textbox")[0];
89
+ const c = await getThumbnailImage(dom, {
90
+ ...options,
91
+ allowTaint: true,
92
+ backgroundColor: null,
93
+ proxy: "anonymous",
94
+ useCORS: true,
95
+ onclone: document => {
96
+ // hide class
97
+ for (let hidedeClass of PREVIEW_IMAGE_HIDE_CLASS) {
98
+ for (let element of document.getElementsByClassName(hidedeClass)) {
99
+ element.style.display = "none";
100
+ }
101
+ }
102
+ return document;
103
+ }
104
+ });
105
+ return c;
106
+ };
81
107
  useImperativeHandle(ref, () => ({
82
- async getThumbnail() {
108
+ async getThumbnail(needBackground = false, options = {}) {
83
109
  try {
84
- const c = await html2canvas(editorWrapper?.current);
85
- if (c) {
86
- return c.toDataURL();
87
- }
110
+ const c = await getPreviewImage(needBackground, options);
111
+ return c;
88
112
  } catch (err) {
89
113
  console.log(err);
90
114
  return null;
@@ -118,27 +142,8 @@ const CommonEditor = /*#__PURE__*/forwardRef((props, ref) => {
118
142
  const chars = CHARACTERS.filter(c => c.toLowerCase().startsWith(search?.toLowerCase())).slice(0, 10);
119
143
  const handleEditorChange = newValue => {
120
144
  setValue(newValue);
121
- const isAstChange = editor.operations.some(op => "set_selection" !== op.type);
122
- if (isAstChange && onSave && timeoutInMS === 0) {
123
- // send the value to onSave api
124
- updateChanges(newValue);
125
- }
126
- };
127
- const updateChanges = newValue => {
128
- const stringify = JSON.stringify(newValue);
129
- const text = serialize(newValue);
130
- if (stringify !== lastUpdated && count > 0 && text?.trim().length > 3 && onSave) {
131
- if (editorWrapper && editorWrapper?.current) {
132
- html2canvas(editorWrapper?.current, {
133
- height: 350
134
- }).then(c => {
135
- onSave(stringify, {
136
- text: text,
137
- thumbnail: c.toDataURL()
138
- });
139
- setLastUpdated(stringify);
140
- });
141
- }
145
+ if (!isInteracted) {
146
+ setIsInteracted(true);
142
147
  }
143
148
  };
144
149
  const customProps = {
@@ -211,7 +216,8 @@ const CommonEditor = /*#__PURE__*/forwardRef((props, ref) => {
211
216
  paddingTop: `${bannerSpacing?.top}px`,
212
217
  paddingBottom: `${bannerSpacing?.bottom}px`
213
218
  },
214
- children: [/*#__PURE__*/_jsx(Editable, {
219
+ children: [/*#__PURE__*/_jsx(PopupTool, {}), /*#__PURE__*/_jsx(Editable, {
220
+ className: "innert-editor-textbox",
215
221
  readOnly: isReadOnly,
216
222
  placeholder: "Write something",
217
223
  renderElement: renderElement,
@@ -1,9 +1,5 @@
1
1
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap');
2
2
 
3
- * {
4
- font-family: 'Inter', sans-serif;
5
- }
6
-
7
3
  .ml-1 {
8
4
  margin-left: 10px;
9
5
  }
@@ -437,4 +433,8 @@ html{
437
433
 
438
434
  .toolbar svg {
439
435
  /* fill: 'red' */
436
+ }
437
+
438
+ .MuiIconButton-root.btnActive {
439
+ background-color: #ccc;
440
440
  }
@@ -1,31 +1,23 @@
1
1
  import React, { useRef, useState } from "react";
2
- import { MdFormatColorText, MdFormatColorFill } from "react-icons/md";
3
- import "./ColorPicker.css";
2
+ import { ReactEditor } from "slate-react";
3
+ import Button from "../../common/Button";
4
4
  import { colors } from "./defaultColors";
5
5
  import { addMarkData, activeMark } from "../../utils/SlateUtilityFunctions";
6
6
  import { Transforms } from "slate";
7
7
  import usePopup from "../../utils/customHooks/usePopup";
8
- import { ReactEditor } from "slate-react";
8
+ import { logo } from "./LogoIcon";
9
+ import "./ColorPicker.css";
9
10
  import { jsx as _jsx } from "react/jsx-runtime";
10
11
  import { jsxs as _jsxs } from "react/jsx-runtime";
11
12
  import { Fragment as _Fragment } from "react/jsx-runtime";
12
- const logo = {
13
- color: /*#__PURE__*/_jsx(MdFormatColorText, {
14
- size: 17,
15
- style: {
16
- fill: '#64748B'
17
- }
18
- }),
19
- bgColor: /*#__PURE__*/_jsx(MdFormatColorFill, {
20
- size: 17,
21
- style: {
22
- fill: '#64748B'
23
- }
24
- })
13
+ const DEFAULT_COLOR = {
14
+ color: "#000000",
15
+ bgcolor: "#FFFFFF"
25
16
  };
26
17
  const ColorPicker = ({
27
18
  format,
28
- editor
19
+ editor,
20
+ showHex
29
21
  }) => {
30
22
  const [selection, setSelection] = useState();
31
23
  const [hexValue, setHexValue] = useState("");
@@ -68,17 +60,27 @@ const ColorPicker = ({
68
60
  setValidHex(isValideHexSix.test(newHex) || isValideHexThree.test(newHex));
69
61
  setHexValue(newHex);
70
62
  };
63
+ const activeColor = showOptions ? DEFAULT_COLOR[format] : activeMark(editor, format);
71
64
  return /*#__PURE__*/_jsxs("div", {
72
65
  className: "color-picker popup-wrapper1 color-picker-dialog",
73
66
  ref: colorPickerRef,
74
- children: [/*#__PURE__*/_jsx("button", {
67
+ style: {
68
+ display: "flex",
69
+ alignItems: "center"
70
+ },
71
+ children: [showHex ? /*#__PURE__*/_jsx("div", {
72
+ style: {
73
+ display: "flex"
74
+ },
75
+ children: activeColor
76
+ }) : null, /*#__PURE__*/_jsx(Button, {
75
77
  style: {
76
- color: showOptions ? "black" : activeMark(editor, format),
78
+ color: activeColor,
77
79
  opacity: "1"
78
80
  },
79
81
  className: showOptions ? "clicked" : "",
80
82
  onClick: toggleOption,
81
- children: logo[format]
83
+ children: logo[format](activeColor)
82
84
  }), showOptions && /*#__PURE__*/_jsxs(_Fragment, {
83
85
  children: [/*#__PURE__*/_jsx("div", {
84
86
  className: "backdrop",
@@ -125,7 +127,7 @@ const ColorPicker = ({
125
127
  className: "colorSaveBtn",
126
128
  style: {
127
129
  background: validHex ? "#2563EB" : "#64748B",
128
- color: '#fff'
130
+ color: "#fff"
129
131
  },
130
132
  type: "submit",
131
133
  children: "Save"
@@ -0,0 +1,59 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { jsxs as _jsxs } from "react/jsx-runtime";
3
+ export const logo = {
4
+ color: color => /*#__PURE__*/_jsx("svg", {
5
+ xmlns: "http://www.w3.org/2000/svg",
6
+ fill: "#000000",
7
+ height: "20px",
8
+ width: "20px",
9
+ version: "1.1",
10
+ id: "Icons",
11
+ viewBox: "0 0 32 32",
12
+ children: /*#__PURE__*/_jsxs("g", {
13
+ children: [/*#__PURE__*/_jsx("path", {
14
+ d: "M29,27H3c-0.6,0-1,0.4-1,1s0.4,1,1,1h26c0.6,0,1-0.4,1-1S29.6,27,29,27z",
15
+ fill: color || "#000"
16
+ }), /*#__PURE__*/_jsx("path", {
17
+ d: "M5,24h4c0.6,0,1-0.4,1-1s-0.4-1-1-1H8.6l1.9-4h11.1l1.9,4H23c-0.6,0-1,0.4-1,1s0.4,1,1,1h4c0.6,0,1-0.4,1-1s-0.4-1-1-1 h-1.4L16.9,3.6C16.7,3.2,16.4,3,16,3s-0.7,0.2-0.9,0.6L6.4,22H5c-0.6,0-1,0.4-1,1S4.4,24,5,24z M16,6.3l4.6,9.7h-9.2L16,6.3z"
18
+ })]
19
+ })
20
+ }),
21
+ bgColor: color => /*#__PURE__*/_jsxs("svg", {
22
+ xmlns: "http://www.w3.org/2000/svg",
23
+ width: "24px",
24
+ height: "24px",
25
+ viewBox: "0 0 50 50",
26
+ fill: "none",
27
+ children: [/*#__PURE__*/_jsx("rect", {
28
+ width: "48",
29
+ height: "48",
30
+ fill: "white",
31
+ fillOpacity: "0.01"
32
+ }), /*#__PURE__*/_jsx("path", {
33
+ fillRule: "evenodd",
34
+ clipRule: "evenodd",
35
+ d: "M37 37C39.2091 37 41 35.2091 41 33C41 31.5272 39.6667 29.5272 37 27C34.3333 29.5272 33 31.5272 33 33C33 35.2091 34.7909 37 37 37Z",
36
+ fill: "#000000"
37
+ }), /*#__PURE__*/_jsx("path", {
38
+ d: "M20.8535 5.50439L24.389 9.03993",
39
+ stroke: "#000000",
40
+ strokeWidth: "4",
41
+ strokeLinecap: "round"
42
+ }), /*#__PURE__*/_jsx("path", {
43
+ d: "M23.6818 8.33281L8.12549 23.8892L19.4392 35.2029L34.9955 19.6465L23.6818 8.33281Z",
44
+ stroke: "#000000",
45
+ strokeWidth: "4",
46
+ strokeLinejoin: "round"
47
+ }), /*#__PURE__*/_jsx("path", {
48
+ d: "M12 20.0732L28.961 25.6496",
49
+ stroke: "#000000",
50
+ strokeWidth: "4",
51
+ strokeLinecap: "round"
52
+ }), /*#__PURE__*/_jsx("path", {
53
+ d: "M4 43H44",
54
+ stroke: color || "#FFF",
55
+ strokeWidth: "4",
56
+ strokeLinecap: "round"
57
+ })]
58
+ })
59
+ };
@@ -0,0 +1,16 @@
1
+ import React from "react";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ const Title = props => {
4
+ const {
5
+ attributes,
6
+ children
7
+ } = props;
8
+ return /*#__PURE__*/_jsx("div", {
9
+ ...attributes,
10
+ style: {
11
+ fontWeight: "bold"
12
+ },
13
+ children: children
14
+ });
15
+ };
16
+ export default Title;
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ import Icon from "../../common/Icon";
3
+ import Button from "../../common/Button";
4
+ import { toggleBlock, isBlockActive } from "../../utils/SlateUtilityFunctions.js";
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ const BlockButton = ({
7
+ editor,
8
+ format
9
+ }) => {
10
+ return /*#__PURE__*/_jsx(Button, {
11
+ active: isBlockActive(editor, format),
12
+ format: format,
13
+ onMouseDown: e => {
14
+ e.preventDefault();
15
+ toggleBlock(editor, format);
16
+ },
17
+ children: /*#__PURE__*/_jsx(Icon, {
18
+ icon: format
19
+ })
20
+ });
21
+ };
22
+ export default BlockButton;
@@ -0,0 +1,40 @@
1
+ import React from "react";
2
+ import { Select, MenuItem } from "@mui/material";
3
+ import { addMarkData, activeMark } from "../../utils/SlateUtilityFunctions.js";
4
+ import { fontFamilyMap } from "../../utils/font";
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ const Dropdown = ({
7
+ editor,
8
+ format,
9
+ options
10
+ }) => {
11
+ const value = activeMark(editor, format);
12
+ const changeMarkData = (event, format) => {
13
+ event.preventDefault();
14
+ const value = event.target.value;
15
+ addMarkData(editor, {
16
+ format,
17
+ value
18
+ });
19
+ };
20
+ return /*#__PURE__*/_jsx(Select, {
21
+ value: value,
22
+ className: "editor-dd",
23
+ onChange: e => changeMarkData(e, format),
24
+ style: {
25
+ fontFamily: fontFamilyMap[value],
26
+ width: "100%",
27
+ height: "36px",
28
+ overflow: "hidden",
29
+ borderRadius: "10px"
30
+ },
31
+ children: options.map((item, index) => /*#__PURE__*/_jsx(MenuItem, {
32
+ style: {
33
+ fontFamily: item.text
34
+ },
35
+ value: item.value,
36
+ children: item.text
37
+ }, index))
38
+ });
39
+ };
40
+ export default Dropdown;
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ import Icon from "../../common/Icon";
3
+ import Button from "../../common/Button";
4
+ import { toggleMark, isMarkActive } from "../../utils/SlateUtilityFunctions.js";
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ const MarkButton = ({
7
+ editor,
8
+ format
9
+ }) => {
10
+ return /*#__PURE__*/_jsx(Button, {
11
+ active: isMarkActive(editor, format),
12
+ format: format,
13
+ onMouseDown: e => {
14
+ e.preventDefault();
15
+ toggleMark(editor, format);
16
+ },
17
+ children: /*#__PURE__*/_jsx(Icon, {
18
+ icon: format
19
+ })
20
+ });
21
+ };
22
+ export default MarkButton;
@@ -0,0 +1,4 @@
1
+ import BlockButton from "./BlockButton";
2
+ import Dropdown from "./Dropdown";
3
+ import MarkButton from "./MarkButton";
4
+ export { BlockButton, Dropdown, MarkButton };
@@ -0,0 +1,34 @@
1
+ const usePopupStyle = () => ({
2
+ popupWrapper: {
3
+ boxShadow: "0px 4px 10px 0px rgba(0, 0, 0, 0.16)"
4
+ },
5
+ textFormatWrapper: {
6
+ padding: "0px 24px 24px 24px",
7
+ width: "350px"
8
+ },
9
+ textFormatLabel: {
10
+ display: "flex",
11
+ alignItems: "center",
12
+ fontWeight: 600,
13
+ fontSize: "14px",
14
+ lineHeight: "25px",
15
+ marginTop: "8px",
16
+ marginBottom: "8px",
17
+ color: "rgba(169, 169, 169, 1)"
18
+ },
19
+ textFormatField: {
20
+ marginBottom: "8px"
21
+ },
22
+ textFormatFieldBorder: {
23
+ display: "flex",
24
+ alignItems: "center",
25
+ marginBottom: "8px",
26
+ border: "1px solid rgba(169, 169, 169, 1)",
27
+ padding: "0px 8px",
28
+ borderRadius: "10px"
29
+ },
30
+ textFormatCG: {
31
+ marginBottom: "8px"
32
+ }
33
+ });
34
+ export default usePopupStyle;
@@ -0,0 +1,81 @@
1
+ import React from "react";
2
+ import { Grid } from "@mui/material";
3
+ import toolbarGroups from "../toolbarGroups";
4
+ import { Dropdown, MarkButton } from "../FormatTools";
5
+ import ColorPicker from "../../Elements/Color Picker/ColorPicker";
6
+ import { activeMark } from "../../utils/SlateUtilityFunctions";
7
+ import { jsx as _jsx } from "react/jsx-runtime";
8
+ import { jsxs as _jsxs } from "react/jsx-runtime";
9
+ const allTools = toolbarGroups.flat();
10
+ const TextFormat = props => {
11
+ const {
12
+ classes,
13
+ editor
14
+ } = props;
15
+ const fontFamily = allTools.find(f => f.format === "fontFamily");
16
+ const fontStyle = allTools.filter(f => f.type === "mark");
17
+ return /*#__PURE__*/_jsxs(Grid, {
18
+ container: true,
19
+ sx: classes.textFormatWrapper,
20
+ children: [/*#__PURE__*/_jsx(Grid, {
21
+ item: true,
22
+ xs: 12,
23
+ sx: classes.textFormatLabel,
24
+ children: "TEXT COLOR"
25
+ }), /*#__PURE__*/_jsx(Grid, {
26
+ item: true,
27
+ xs: 12,
28
+ sx: classes.textFormatFieldBorder,
29
+ children: /*#__PURE__*/_jsx(ColorPicker, {
30
+ format: "color",
31
+ activeMark: activeMark,
32
+ editor: editor,
33
+ showHex: true
34
+ })
35
+ }), /*#__PURE__*/_jsx(Grid, {
36
+ item: true,
37
+ xs: 12,
38
+ sx: classes.textFormatLabel,
39
+ children: "BACKGROUND COLOR"
40
+ }), /*#__PURE__*/_jsx(Grid, {
41
+ item: true,
42
+ xs: 12,
43
+ sx: classes.textFormatFieldBorder,
44
+ children: /*#__PURE__*/_jsx(ColorPicker, {
45
+ format: "bgColor",
46
+ activeMark: activeMark,
47
+ editor: editor,
48
+ showHex: true
49
+ })
50
+ }), /*#__PURE__*/_jsx(Grid, {
51
+ item: true,
52
+ xs: 12,
53
+ sx: classes.textFormatLabel,
54
+ children: "FONT FAMILY"
55
+ }), /*#__PURE__*/_jsx(Grid, {
56
+ item: true,
57
+ xs: 12,
58
+ sx: classes.textFormatField,
59
+ children: /*#__PURE__*/_jsx(Dropdown, {
60
+ ...fontFamily,
61
+ editor: editor
62
+ })
63
+ }), /*#__PURE__*/_jsx(Grid, {
64
+ item: true,
65
+ xs: 12,
66
+ sx: classes.textFormatLabel,
67
+ children: "FONT STYLE"
68
+ }), /*#__PURE__*/_jsx(Grid, {
69
+ item: true,
70
+ xs: 12,
71
+ sx: classes.textFormatCG,
72
+ children: fontStyle?.map((m, i) => {
73
+ return /*#__PURE__*/_jsx(MarkButton, {
74
+ editor: editor,
75
+ ...m
76
+ }, `pptool_mark_${i}_${m.id}`);
77
+ })
78
+ })]
79
+ });
80
+ };
81
+ export default TextFormat;
@@ -0,0 +1,69 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Popper, Fade, Paper, IconButton } from "@mui/material";
3
+ import CloseIcon from "@mui/icons-material/Close";
4
+ import { Editor, Range } from "slate";
5
+ import { useSlate, useFocused } from "slate-react";
6
+ import TextFormat from "./TextFormat";
7
+ import usePopupStyle from "./PopupToolStyle";
8
+ import { jsx as _jsx } from "react/jsx-runtime";
9
+ import { jsxs as _jsxs } from "react/jsx-runtime";
10
+ const PopupTool = () => {
11
+ const classes = usePopupStyle();
12
+ const [anchorEl, setAnchorEl] = useState(null);
13
+ const editor = useSlate();
14
+ const inFocus = useFocused();
15
+ const {
16
+ selection
17
+ } = editor;
18
+ const open = Boolean(anchorEl);
19
+ const id = open ? "popup-edit-tool" : "";
20
+ useEffect(() => {
21
+ if (!selection || !inFocus || Range.isCollapsed(selection) || Editor.string(editor, selection) === "") {
22
+ setAnchorEl(null);
23
+ } else {
24
+ const domSelection = window.getSelection();
25
+ const domRange = domSelection.getRangeAt(0);
26
+ const rect = domRange.getBoundingClientRect();
27
+ setAnchorEl({
28
+ clientWidth: rect.width,
29
+ clientHeight: rect.height,
30
+ getBoundingClientRect: () => rect
31
+ });
32
+ }
33
+ }, [selection]);
34
+ const handleClose = () => {
35
+ setAnchorEl(null);
36
+ };
37
+ return /*#__PURE__*/_jsx(Popper, {
38
+ id: id,
39
+ open: open,
40
+ disablePortal: false,
41
+ anchorEl: anchorEl,
42
+ transition: true,
43
+ placement: "bottom-start",
44
+ onMouseDown: e => e.preventDefault(),
45
+ sx: classes.popupWrapper,
46
+ children: ({
47
+ TransitionProps
48
+ }) => /*#__PURE__*/_jsx(Fade, {
49
+ ...TransitionProps,
50
+ timeout: 350,
51
+ children: /*#__PURE__*/_jsxs(Paper, {
52
+ children: [/*#__PURE__*/_jsx("div", {
53
+ style: {
54
+ display: "flex",
55
+ justifyContent: "end"
56
+ },
57
+ children: /*#__PURE__*/_jsx(IconButton, {
58
+ onClick: handleClose,
59
+ children: /*#__PURE__*/_jsx(CloseIcon, {})
60
+ })
61
+ }), /*#__PURE__*/_jsx(TextFormat, {
62
+ editor: editor,
63
+ classes: classes
64
+ })]
65
+ })
66
+ })
67
+ });
68
+ };
69
+ export default PopupTool;
@@ -1,13 +1,9 @@
1
1
  import React, { useEffect, useState } from "react";
2
- import { Select, MenuItem } from "@mui/material";
3
2
  import { useSlate } from "slate-react";
4
- import Button from "../common/Button";
5
- import Icon from "../common/Icon";
6
- import { toggleBlock, toggleMark, isMarkActive, addMarkData, isBlockActive, activeMark } from "../utils/SlateUtilityFunctions.js";
7
- import { fontFamilyMap } from "../utils/font";
3
+ import { isBlockActive, activeMark } from "../utils/SlateUtilityFunctions.js";
8
4
  import useFormat from "../utils/customHooks/useFormat.js";
9
5
  import defaultToolbarGroups from "./toolbarGroups.js";
10
- import "./styles.css";
6
+ import { BlockButton, MarkButton, Dropdown } from "./FormatTools";
11
7
  import ColorPicker from "../Elements/Color Picker/ColorPicker";
12
8
  import LinkButton from "../Elements/Link/LinkButton";
13
9
  import Embed from "../Elements/Embed/Embed";
@@ -26,6 +22,7 @@ import CarouselButton from "../Elements/Carousel/CarouselButton";
26
22
  import ChipTextButton from "../Elements/ChipText/ChipTextButton";
27
23
  import DrawerMenuButton from "../Elements/DrawerMenu/DrawerMenuButton";
28
24
  import AppHeaderButton from "../Elements/AppHeader/AppHeaderButton";
25
+ import "./styles.css";
29
26
  import { jsx as _jsx } from "react/jsx-runtime";
30
27
  import { jsxs as _jsxs } from "react/jsx-runtime";
31
28
  const Toolbar = props => {
@@ -48,68 +45,6 @@ const Toolbar = props => {
48
45
  setToolbarGroups(filteredGroups);
49
46
  // eslint-disable-next-line react-hooks/exhaustive-deps
50
47
  }, [isTable]);
51
- const BlockButton = ({
52
- format
53
- }) => {
54
- return /*#__PURE__*/_jsx(Button, {
55
- active: isBlockActive(editor, format),
56
- format: format,
57
- onMouseDown: e => {
58
- e.preventDefault();
59
- toggleBlock(editor, format);
60
- },
61
- children: /*#__PURE__*/_jsx(Icon, {
62
- icon: format
63
- })
64
- });
65
- };
66
- const MarkButton = ({
67
- format
68
- }) => {
69
- return /*#__PURE__*/_jsx(Button, {
70
- active: isMarkActive(editor, format),
71
- format: format,
72
- onMouseDown: e => {
73
- e.preventDefault();
74
- toggleMark(editor, format);
75
- },
76
- children: /*#__PURE__*/_jsx(Icon, {
77
- icon: format
78
- })
79
- });
80
- };
81
- const Dropdown = ({
82
- format,
83
- options
84
- }) => {
85
- const value = activeMark(editor, format);
86
- return /*#__PURE__*/_jsx(Select, {
87
- value: value,
88
- className: "editor-dd",
89
- onChange: e => changeMarkData(e, format),
90
- style: {
91
- fontFamily: fontFamilyMap[value],
92
- width: "200px",
93
- height: "36px",
94
- overflow: "hidden"
95
- },
96
- children: options.map((item, index) => /*#__PURE__*/_jsx(MenuItem, {
97
- style: {
98
- fontFamily: item.text
99
- },
100
- value: item.value,
101
- children: item.text
102
- }, index))
103
- });
104
- };
105
- const changeMarkData = (event, format) => {
106
- event.preventDefault();
107
- const value = event.target.value;
108
- addMarkData(editor, {
109
- format,
110
- value
111
- });
112
- };
113
48
  return /*#__PURE__*/_jsxs("div", {
114
49
  className: "toolbar",
115
50
  children: [toolbarGroups.map((group, index) => /*#__PURE__*/_jsx("span", {
@@ -118,15 +53,18 @@ const Toolbar = props => {
118
53
  switch (element.type) {
119
54
  case "block":
120
55
  return /*#__PURE__*/_jsx(BlockButton, {
121
- ...element
56
+ ...element,
57
+ editor: editor
122
58
  }, element.id);
123
59
  case "mark":
124
60
  return /*#__PURE__*/_jsx(MarkButton, {
125
- ...element
61
+ ...element,
62
+ editor: editor
126
63
  }, element.id);
127
64
  case "dropdown":
128
65
  return /*#__PURE__*/_jsx(Dropdown, {
129
- ...element
66
+ ...element,
67
+ editor: editor
130
68
  }, element.id);
131
69
  case "link":
132
70
  return /*#__PURE__*/_jsx(LinkButton, {
@@ -0,0 +1,10 @@
1
+ import html2canvas from "html2canvas";
2
+ export const getThumbnailImage = async (dom, options = {}) => {
3
+ try {
4
+ const canvas = await html2canvas(dom, options);
5
+ return canvas.toDataURL();
6
+ } catch (err) {
7
+ console.log(err);
8
+ return null;
9
+ }
10
+ };
@@ -5,7 +5,8 @@ import withTables from "../plugins/withTable";
5
5
  import withEmbeds from "../plugins/withEmbeds";
6
6
  import withEquation from "../plugins/withEquation";
7
7
  import withMentions from "../plugins/withMentions";
8
+ import withLayout from "../plugins/withLayout";
8
9
  const withCommon = props => {
9
- return withEquation(withHistory(withEmbeds(withTables(withLinks(withMentions(withReact(props)))))));
10
+ return withEquation(withLayout(withHistory(withEmbeds(withTables(withLinks(withMentions(withReact(props))))))));
10
11
  };
11
12
  export default withCommon;
@@ -0,0 +1,62 @@
1
+ import { Transforms, Node, Element as SlateElement, Editor } from "slate";
2
+ const withLayout = editor => {
3
+ const {
4
+ normalizeNode
5
+ } = editor;
6
+ editor.normalizeNode = ([node, path]) => {
7
+ if (path.length === 0) {
8
+ if (editor.children.length <= 1 && Editor.string(editor, [0, 0]) === "") {
9
+ const title = {
10
+ type: "title",
11
+ children: [{
12
+ text: "Untitled"
13
+ }]
14
+ };
15
+ Transforms.insertNodes(editor, title, {
16
+ at: path.concat(0),
17
+ select: true
18
+ });
19
+ }
20
+ if (editor.children.length < 2) {
21
+ const paragraph = {
22
+ type: "paragraph",
23
+ children: [{
24
+ text: ""
25
+ }]
26
+ };
27
+ Transforms.insertNodes(editor, paragraph, {
28
+ at: path.concat(1)
29
+ });
30
+ }
31
+ for (const [child, childPath] of Node.children(editor, path)) {
32
+ let type = "";
33
+ const slateIndex = childPath[0];
34
+ const enforceType = type => {
35
+ if (SlateElement.isElement(child) && child.type !== type) {
36
+ const newProperties = {
37
+ type
38
+ };
39
+ Transforms.setNodes(editor, newProperties, {
40
+ at: childPath
41
+ });
42
+ }
43
+ };
44
+ switch (slateIndex) {
45
+ case 0:
46
+ type = "title";
47
+ enforceType(type);
48
+ break;
49
+ case 1:
50
+ type = "paragraph";
51
+ enforceType(type);
52
+ break;
53
+ default:
54
+ break;
55
+ }
56
+ }
57
+ }
58
+ return normalizeNode([node, path]);
59
+ };
60
+ return editor;
61
+ };
62
+ export default withLayout;
@@ -25,6 +25,7 @@ import ChipText from "../Elements/ChipText/ChipText";
25
25
  import DrawerMenu from "../Elements/DrawerMenu/DrawerMenu";
26
26
  import AppHeader from "../Elements/AppHeader/AppHeader";
27
27
  import PageSettings from "../Elements/PageSettings/PageSettings";
28
+ import Title from "../Elements/Title/title";
28
29
  import { jsx as _jsx } from "react/jsx-runtime";
29
30
  const alignment = ["alignLeft", "alignRight", "alignCenter"];
30
31
  const list_types = ["orderedList", "unorderedList"];
@@ -93,8 +94,8 @@ export const isBlockActive = (editor, format) => {
93
94
  };
94
95
  export const activeMark = (editor, format) => {
95
96
  const defaultMarkData = {
96
- color: "black",
97
- bgColor: "black",
97
+ color: "#000000",
98
+ bgColor: "#FFFFFF",
98
99
  fontSize: "normal",
99
100
  fontFamily: "sans"
100
101
  };
@@ -363,6 +364,10 @@ export const getBlock = props => {
363
364
  return /*#__PURE__*/_jsx(PageSettings, {
364
365
  ...props
365
366
  });
367
+ case "title":
368
+ return /*#__PURE__*/_jsx(Title, {
369
+ ...props
370
+ });
366
371
  default:
367
372
  return /*#__PURE__*/_jsx("div", {
368
373
  ...element.attr,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flozy/editor",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "An Editor for flozy app brain",
5
5
  "files": [
6
6
  "dist"
@@ -34,6 +34,7 @@
34
34
  "slate-history": "^0.93.0",
35
35
  "slate-react": "^0.98.3",
36
36
  "styled-components": "^5.3.11",
37
+ "use-debounce": "^10.0.0",
37
38
  "web-vitals": "^2.1.4",
38
39
  "y-websocket": "^1.5.0",
39
40
  "yjs": "^13.6.8"
@@ -1,21 +0,0 @@
1
- import { useEffect, useState } from "react";
2
- let t = null;
3
- const useTimeout = props => {
4
- const {
5
- timeoutInMS
6
- } = props;
7
- const [count, setCount] = useState(-1);
8
- const onReset = () => {
9
- clearTimeout(t);
10
- t = setTimeout(() => {
11
- setCount(count + 1);
12
- }, timeoutInMS);
13
- };
14
- useEffect(() => {
15
- if (timeoutInMS) {
16
- onReset();
17
- }
18
- }, [timeoutInMS, count]);
19
- return [count];
20
- };
21
- export default useTimeout;