@flozy/editor 3.1.0 → 3.1.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.
@@ -408,7 +408,7 @@ const CommonEditor = /*#__PURE__*/forwardRef((props, ref) => {
408
408
  children: [/*#__PURE__*/_jsx(DragAndDrop, {
409
409
  children: /*#__PURE__*/_jsx(Overlay, {
410
410
  children: /*#__PURE__*/_jsx(Box, {
411
- className: `${hasTopBanner() ? "has-topbanner" : ""} ${!pageColor ? "no-color" : ""} ${isScrolling ? "" : "hideScroll"} scrollable-content`,
411
+ className: `${hasTopBanner() ? "has-topbanner" : ""} ${!pageColor ? "no-color" : ""} ${isScrolling ? "" : "hideScroll"} scrollable-content scrollSmooth`,
412
412
  sx: classes.slateWrapper,
413
413
  id: "slate-wrapper-scroll-container",
414
414
  style: editorWrapperStyle,
@@ -1109,3 +1109,7 @@ blockquote {
1109
1109
  .doublequote::after {
1110
1110
  content: '\201D';
1111
1111
  }
1112
+
1113
+ .scrollSmooth {
1114
+ scroll-behavior: smooth;
1115
+ }
@@ -0,0 +1,219 @@
1
+ import { Autocomplete, Checkbox, FormControlLabel, MenuItem, Select, TextField, Typography, createFilterOptions } from "@mui/material";
2
+ import { useEffect, useMemo, useState } from "react";
3
+ import { jsx as _jsx } from "react/jsx-runtime";
4
+ import { Fragment as _Fragment } from "react/jsx-runtime";
5
+ import { jsxs as _jsxs } from "react/jsx-runtime";
6
+ const OpenInNewTab = props => {
7
+ const {
8
+ nav,
9
+ openInNewTab,
10
+ onNewTabChange
11
+ } = props;
12
+ return nav?.showOpenInNewTab ? /*#__PURE__*/_jsx(FormControlLabel, {
13
+ className: "ccheckbox-primary",
14
+ control: /*#__PURE__*/_jsx(Checkbox, {
15
+ checked: openInNewTab,
16
+ onChange: onNewTabChange
17
+ }),
18
+ label: /*#__PURE__*/_jsx(Typography, {
19
+ variant: "body2",
20
+ children: "Open in new tab"
21
+ })
22
+ }) : null;
23
+ };
24
+ export const TextInput = props => {
25
+ return /*#__PURE__*/_jsxs(_Fragment, {
26
+ children: [/*#__PURE__*/_jsx(TextField, {
27
+ fullWidth: true,
28
+ size: "small",
29
+ ...props,
30
+ onChange: e => {
31
+ props.onChange(e.target.value);
32
+ }
33
+ }), /*#__PURE__*/_jsx(OpenInNewTab, {
34
+ ...props
35
+ })]
36
+ });
37
+ };
38
+ export const SelectPage = props => {
39
+ const {
40
+ value,
41
+ onChange,
42
+ services
43
+ } = props;
44
+ const [pages, setPages] = useState([]);
45
+ const getPages = async () => {
46
+ const result = await services("getPages", {});
47
+ const refactor = result?.data?.map(r => {
48
+ const {
49
+ title,
50
+ url_name,
51
+ ...rest
52
+ } = r;
53
+ return {
54
+ label: title,
55
+ value: url_name,
56
+ ...rest
57
+ };
58
+ });
59
+ setPages(refactor);
60
+ };
61
+ useEffect(() => {
62
+ getPages();
63
+ }, []);
64
+ const [page, section] = useMemo(() => {
65
+ if (value) {
66
+ const [page, section] = value.split("#");
67
+ const selectedPage = pages.find(p => p.value === page) || {
68
+ label: page,
69
+ value: page
70
+ };
71
+ const selectedSection = pages.find(p => p.value === section) || {
72
+ label: section,
73
+ value: section
74
+ };
75
+ return [selectedPage, selectedSection];
76
+ }
77
+ return [];
78
+ }, [value, pages]);
79
+ return /*#__PURE__*/_jsxs("div", {
80
+ children: [/*#__PURE__*/_jsx(FreeSoloCreateOption, {
81
+ label: page?.label,
82
+ setValue: val => onChange(val?.value),
83
+ placeholder: "Select Page",
84
+ options: pages
85
+ }), /*#__PURE__*/_jsx(FreeSoloCreateOption, {
86
+ label: section?.label,
87
+ setValue: val => {
88
+ let url = page?.value;
89
+ if (val?.value) {
90
+ url += `#${val?.value}`;
91
+ }
92
+ onChange(url);
93
+ },
94
+ placeholder: "Select Section or anchor",
95
+ options: page?.sections?.map(p => ({
96
+ label: p,
97
+ value: p
98
+ }))
99
+ }), /*#__PURE__*/_jsx(OpenInNewTab, {
100
+ ...props
101
+ })]
102
+ });
103
+ };
104
+ export const Trigger = props => {
105
+ return /*#__PURE__*/_jsx(Typography, {
106
+ variant: "h6",
107
+ gutterBottom: true,
108
+ children: "Choosing this will trigger the next step"
109
+ });
110
+ };
111
+ const scrollToOptions = [{
112
+ label: "Top",
113
+ value: "top"
114
+ }, {
115
+ label: "Bottom",
116
+ value: "bottom"
117
+ }];
118
+ export const ScrollTopBottom = props => {
119
+ const {
120
+ value,
121
+ onChange
122
+ } = props;
123
+ return /*#__PURE__*/_jsxs(_Fragment, {
124
+ children: [/*#__PURE__*/_jsx(Typography, {
125
+ variant: "body2",
126
+ sx: {
127
+ paddingBottom: "4px"
128
+ },
129
+ children: "Choose Top/Bottom of page"
130
+ }), /*#__PURE__*/_jsx(Select, {
131
+ size: "small",
132
+ fullWidth: true,
133
+ value: value,
134
+ onChange: e => onChange(e.target.value),
135
+ children: scrollToOptions.map((option, i) => {
136
+ return /*#__PURE__*/_jsx(MenuItem, {
137
+ value: option.value,
138
+ children: /*#__PURE__*/_jsx(Typography, {
139
+ variant: "body2",
140
+ children: option.label
141
+ })
142
+ }, i);
143
+ })
144
+ })]
145
+ });
146
+ };
147
+ const filter = createFilterOptions();
148
+ export function FreeSoloCreateOption({
149
+ label,
150
+ setValue,
151
+ options = [],
152
+ placeholder = ""
153
+ }) {
154
+ return /*#__PURE__*/_jsx(Autocomplete, {
155
+ freeSolo: true,
156
+ options: options,
157
+ value: label || "",
158
+ renderInput: params => /*#__PURE__*/_jsx(TextField, {
159
+ ...params,
160
+ label: placeholder
161
+ }),
162
+ renderOption: (props, option) => /*#__PURE__*/_jsx("li", {
163
+ ...props,
164
+ children: option.label
165
+ }),
166
+ onChange: (event, newValue) => {
167
+ if (typeof newValue === 'string') {
168
+ setValue({
169
+ value: newValue
170
+ });
171
+ } else if (newValue && newValue.inputValue) {
172
+ const {
173
+ inputValue
174
+ } = newValue;
175
+
176
+ // Create a new value from the user input
177
+ setValue({
178
+ label: inputValue,
179
+ value: inputValue
180
+ });
181
+ } else {
182
+ setValue(newValue);
183
+ }
184
+ },
185
+ filterOptions: (options, params) => {
186
+ const filtered = filter(options, params);
187
+ const {
188
+ inputValue
189
+ } = params;
190
+ // Suggest the creation of a new value
191
+ const isExisting = options.some(option => inputValue === option.label);
192
+ if (inputValue !== '' && !isExisting) {
193
+ filtered.push({
194
+ inputValue,
195
+ label: `Add "${inputValue}"`
196
+ });
197
+ }
198
+ return filtered;
199
+ },
200
+ selectOnFocus: true,
201
+ clearOnBlur: true,
202
+ handleHomeEndKeys: true,
203
+ getOptionLabel: option => {
204
+ // Value selected with enter, right from the input
205
+ if (typeof option === 'string') {
206
+ return option;
207
+ }
208
+ // Add "xxx" option created dynamically
209
+ if (option.inputValue) {
210
+ return option.inputValue;
211
+ }
212
+ // Regular option
213
+ return option.value;
214
+ },
215
+ sx: {
216
+ marginTop: "10px"
217
+ }
218
+ });
219
+ }
@@ -0,0 +1,138 @@
1
+ import Button from '@mui/material/Button';
2
+ import Dialog from '@mui/material/Dialog';
3
+ import DialogTitle from '@mui/material/DialogTitle';
4
+ import DialogContent from '@mui/material/DialogContent';
5
+ import DialogActions from '@mui/material/DialogActions';
6
+ import IconButton from '@mui/material/IconButton';
7
+ import CloseIcon from '@mui/icons-material/Close';
8
+ import { FormControl, FormControlLabel, Grid, Radio, RadioGroup } from '@mui/material';
9
+ import { useState } from 'react';
10
+ import ButtonNavSettingsStyles from './style';
11
+ import { getNavOptions } from './navOptions';
12
+ import { ScrollTopBottom, SelectPage, TextInput, Trigger } from './NavComponents';
13
+ import { jsx as _jsx } from "react/jsx-runtime";
14
+ import { jsxs as _jsxs } from "react/jsx-runtime";
15
+ const MAP_COMPONENT = {
16
+ webAddress: TextInput,
17
+ email: TextInput,
18
+ phone: TextInput,
19
+ actionTrigger: Trigger,
20
+ scrollTopOrBottom: ScrollTopBottom,
21
+ page: SelectPage
22
+ };
23
+ export default function ButtonNavSettings(props) {
24
+ const {
25
+ open,
26
+ handleClose,
27
+ onSave,
28
+ customProps,
29
+ element,
30
+ editor
31
+ } = props;
32
+ const navOptions = getNavOptions(customProps.hideTools);
33
+ const prevNavType = element?.buttonLink?.linkType;
34
+ const classes = ButtonNavSettingsStyles();
35
+ const [nav, setNav] = useState(prevNavType ? navOptions.find(n => n.value === prevNavType) : {
36
+ label: "None",
37
+ value: ""
38
+ });
39
+ const [navValue, setNavValue] = useState(element?.url || "");
40
+ const [openInNewTab, setOpenInNewTab] = useState(element.openInNewTab || false);
41
+ const {
42
+ metadata
43
+ } = customProps || {
44
+ metadata: {}
45
+ };
46
+ const {
47
+ buttonLink
48
+ } = metadata || {
49
+ actionTrigger: {}
50
+ };
51
+ const {
52
+ actionTrigger
53
+ } = buttonLink || {};
54
+ const {
55
+ onClick
56
+ } = actionTrigger || {
57
+ options: []
58
+ };
59
+ const NavSettings = MAP_COMPONENT[nav?.value];
60
+ const onChange = value => {
61
+ setNavValue(value);
62
+ };
63
+ return /*#__PURE__*/_jsxs(Dialog, {
64
+ onClose: handleClose,
65
+ open: open,
66
+ sx: classes.dialogContainer,
67
+ fullWidth: true,
68
+ maxWidth: "sm",
69
+ children: [/*#__PURE__*/_jsx(DialogTitle, {
70
+ children: "What do you want to link to?"
71
+ }), /*#__PURE__*/_jsx(IconButton, {
72
+ "aria-label": "close",
73
+ onClick: handleClose,
74
+ sx: classes.closeIcon,
75
+ children: /*#__PURE__*/_jsx(CloseIcon, {})
76
+ }), /*#__PURE__*/_jsx(DialogContent, {
77
+ dividers: true,
78
+ children: /*#__PURE__*/_jsxs(Grid, {
79
+ container: true,
80
+ spacing: 2,
81
+ children: [/*#__PURE__*/_jsx(Grid, {
82
+ item: true,
83
+ xs: 6,
84
+ sx: classes.gridDivider,
85
+ children: /*#__PURE__*/_jsx(FormControl, {
86
+ children: /*#__PURE__*/_jsx(RadioGroup, {
87
+ value: nav?.value,
88
+ children: navOptions?.map((navOption, i) => {
89
+ return /*#__PURE__*/_jsx(FormControlLabel, {
90
+ value: navOption.value,
91
+ control: /*#__PURE__*/_jsx(Radio, {}),
92
+ label: navOption.label,
93
+ onChange: () => {
94
+ setNav(navOption);
95
+ setNavValue("");
96
+ }
97
+ }, i);
98
+ })
99
+ })
100
+ })
101
+ }), /*#__PURE__*/_jsx(Grid, {
102
+ item: true,
103
+ xs: 6,
104
+ children: NavSettings && /*#__PURE__*/_jsx(NavSettings, {
105
+ placeholder: nav?.placeholder,
106
+ nav: nav,
107
+ onChange: onChange,
108
+ value: navValue,
109
+ editor: editor,
110
+ openInNewTab: openInNewTab,
111
+ onNewTabChange: () => setOpenInNewTab(prev => !prev),
112
+ services: customProps.services
113
+ })
114
+ })]
115
+ })
116
+ }), /*#__PURE__*/_jsxs(DialogActions, {
117
+ children: [/*#__PURE__*/_jsx(Button, {
118
+ onClick: handleClose,
119
+ sx: classes.closeBtn,
120
+ children: "Cancel"
121
+ }), /*#__PURE__*/_jsx(Button, {
122
+ onClick: () => {
123
+ onSave({
124
+ buttonLink: {
125
+ linkType: nav?.value,
126
+ onClick
127
+ },
128
+ url: navValue,
129
+ openInNewTab
130
+ });
131
+ handleClose();
132
+ },
133
+ sx: classes.saveBtn,
134
+ children: "Save"
135
+ })]
136
+ })]
137
+ });
138
+ }
@@ -0,0 +1,32 @@
1
+ export const getNavOptions = (hideTools = []) => {
2
+ let navOptions = [{
3
+ label: "None",
4
+ value: ""
5
+ }, {
6
+ label: "Trigger",
7
+ value: "actionTrigger"
8
+ }, {
9
+ label: "Web Address",
10
+ value: "webAddress",
11
+ placeholder: "https://",
12
+ showOpenInNewTab: true
13
+ }, {
14
+ label: "Select Page and Section",
15
+ value: "page",
16
+ placeholder: "Select Page and Section",
17
+ showOpenInNewTab: true
18
+ }, {
19
+ label: "Top/Bottom of page",
20
+ value: "scrollTopOrBottom"
21
+ }, {
22
+ label: "Email",
23
+ value: "email",
24
+ placeholder: "email"
25
+ }, {
26
+ label: "Phone number",
27
+ value: "phone",
28
+ placeholder: "phone"
29
+ }];
30
+ navOptions = navOptions.filter(n => !hideTools.includes(n.value));
31
+ return navOptions;
32
+ };
@@ -0,0 +1,55 @@
1
+ const ButtonNavSettingsStyles = () => ({
2
+ dialogContainer: {
3
+ '& .MuiDialogContent-root': {
4
+ padding: "0px 20px"
5
+ },
6
+ '& .MuiDialogActions-root': {
7
+ padding: "10px"
8
+ },
9
+ '& .MuiTypography-h6': {
10
+ fontWeight: 600,
11
+ fontSize: "16px",
12
+ paddingRight: "20px"
13
+ },
14
+ "& .MuiGrid-container": {
15
+ marginTop: '0px'
16
+ },
17
+ "& .MuiGrid-item": {
18
+ padding: "14px"
19
+ }
20
+ },
21
+ saveBtn: {
22
+ color: '#fff',
23
+ background: "#2563EB",
24
+ fontSize: "14px",
25
+ fontWeight: 500,
26
+ padding: "4px 24px",
27
+ textTransform: "none",
28
+ "&:hover": {
29
+ color: '#fff',
30
+ background: "#2563EB"
31
+ }
32
+ },
33
+ closeBtn: {
34
+ backgroundColor: "#F4F6F9",
35
+ color: "#64748B",
36
+ fontSize: "14px",
37
+ fontWeight: 500,
38
+ padding: "4px 22px",
39
+ textTransform: "none",
40
+ border: "1px solid #D8DDE1",
41
+ "&:hover": {
42
+ border: "1px solid #64748B"
43
+ }
44
+ },
45
+ closeIcon: {
46
+ position: 'absolute',
47
+ right: 8,
48
+ top: 8,
49
+ color: theme => theme.palette.grey[500]
50
+ },
51
+ gridDivider: {
52
+ borderRight: "1px solid rgba(0, 0, 0, 0.12)"
53
+ }
54
+ });
55
+ export default ButtonNavSettingsStyles;
@@ -5,11 +5,13 @@ import { IconButton, Tooltip, Box } from "@mui/material";
5
5
  import * as fIcons from "@mui/icons-material";
6
6
  import SettingsIcon from "@mui/icons-material/Settings";
7
7
  import OpenInNewIcon from "@mui/icons-material/OpenInNew";
8
+ import LinkIcon from "@mui/icons-material/Link";
8
9
  import ButtonPopup from "./ButtonPopup";
9
10
  import { actionButtonRedirect } from "../../service/actionTrigger";
10
11
  import { WorkflowIcon } from "../../common/iconslist";
11
12
  import { getTRBLBreakPoints, getBreakPointsValue } from "../../helper/theme";
12
13
  import { windowVar } from "../../utils/helper";
14
+ import ButtonNavSettings from "./ButtonNavSettings";
13
15
  import { jsx as _jsx } from "react/jsx-runtime";
14
16
  import { jsxs as _jsxs } from "react/jsx-runtime";
15
17
  const EditorButton = props => {
@@ -26,6 +28,7 @@ const EditorButton = props => {
26
28
  const editor = useSlateStatic();
27
29
  const path = ReactEditor.findPath(editor, element);
28
30
  const [edit, setEdit] = useState(false);
31
+ const [openNav, setOpenNav] = useState(false);
29
32
  const {
30
33
  label,
31
34
  bgColor,
@@ -45,7 +48,8 @@ const EditorButton = props => {
45
48
  borderWidth,
46
49
  borderColor,
47
50
  alignment,
48
- xsHidden
51
+ xsHidden,
52
+ openInNewTab
49
53
  } = element;
50
54
  const {
51
55
  linkType,
@@ -54,33 +58,90 @@ const EditorButton = props => {
54
58
  const isTrigger = linkType === "actionTrigger";
55
59
  const BtnIcon = buttonIcon ? fIcons[buttonIcon] : null;
56
60
  windowVar.lastButtonProps = element;
57
- const onClick = async e => {
58
- if (readOnly) {
59
- if (isTrigger) {
60
- if (metadata?.buttonLink?.handler) {
61
- metadata.buttonLink.handler("click");
62
- } else if (redirectOnURLResult) {
63
- // call api and redirect based on api result
64
- const apiResult = await actionButtonRedirect({}, {
65
- url: buttonLink?.url
66
- });
67
- window.open(apiResult, "_blank").focus();
61
+ const handleTrigger = async () => {
62
+ if (metadata?.buttonLink?.handler) {
63
+ metadata.buttonLink.handler("click");
64
+ } else if (redirectOnURLResult) {
65
+ // call api and redirect based on api result
66
+ const apiResult = await actionButtonRedirect({}, {
67
+ url: buttonLink?.url
68
+ });
69
+ window.open(apiResult, "_blank").focus();
70
+ } else {
71
+ const refUrl = buttonLink?.url ? buttonLink?.url.includes("http") ? buttonLink?.url : `//${buttonLink?.url}` : "Link";
72
+ window.open(refUrl, "_blank").focus();
73
+ }
74
+ };
75
+ const handleLinkType = (readOnly, openInNewTab) => {
76
+ const props = {};
77
+ if (!readOnly) {
78
+ return {
79
+ component: "button"
80
+ };
81
+ }
82
+ switch (linkType) {
83
+ case "webAddress":
84
+ const refUrl = url ? url.includes("http") ? url : `//${url}` : "Link";
85
+ props.component = "a";
86
+ if (refUrl !== "Link") {
87
+ props.href = refUrl;
88
+ }
89
+ if (openInNewTab) {
90
+ props.target = "_blank";
91
+ }
92
+ break;
93
+ case "actionTrigger":
94
+ if (readOnly) {
95
+ const {
96
+ url
97
+ } = buttonLink || {};
98
+ const refUrl = url ? url.includes("http") ? url : `//${url}` : "Link";
99
+ props.component = "a";
100
+ if (refUrl !== "Link") {
101
+ props.href = refUrl;
102
+ }
68
103
  } else {
69
- const refUrl = buttonLink?.url ? buttonLink?.url.includes("http") ? buttonLink?.url : `//${buttonLink?.url}` : "Link";
70
- window.open(refUrl, "_blank").focus();
104
+ props.component = "button";
105
+ props.onClick = handleTrigger;
71
106
  }
72
- } else {
73
- const refUrl = url ? url.includes("http") ? url : `//${url}` : "Link";
74
- window.open(refUrl, "_blank").focus();
75
- }
107
+ break;
108
+ case "page":
109
+ props.component = "a";
110
+ const [page, section] = url.split("#");
111
+ props.href = page === "home" ? `#${section}` : `/${url}`;
112
+ if (openInNewTab) {
113
+ props.target = "_blank";
114
+ }
115
+ break;
116
+ case "email":
117
+ props.component = "a";
118
+ props.href = `mailto:${url}`;
119
+ break;
120
+ case "phone":
121
+ props.component = "a";
122
+ props.href = `tel:${url}`;
123
+ break;
124
+ case "scrollTopOrBottom":
125
+ props.component = "button";
126
+ props.onClick = () => {
127
+ const scrollEle = document.getElementById("slate-wrapper-scroll-container");
128
+ if (scrollEle) {
129
+ if (url === "top") {
130
+ // top of the page
131
+ scrollEle.scrollTo(0, 0);
132
+ } else if (url === "bottom") {
133
+ // bottom of the page
134
+ scrollEle.scrollTo(0, scrollEle.scrollHeight);
135
+ }
136
+ }
137
+ };
138
+ break;
76
139
  }
140
+ return props;
77
141
  };
142
+ const buttonProps = handleLinkType(readOnly, openInNewTab);
78
143
  const onMenuClick = val => () => {
79
144
  switch (val) {
80
- case "open":
81
- const refUrl = url ? url.includes("http") ? url : `//${url}` : "Link";
82
- window.open(refUrl, "_blank").focus();
83
- return;
84
145
  case "edit":
85
146
  setEdit(true);
86
147
  return;
@@ -89,16 +150,22 @@ const EditorButton = props => {
89
150
  at: [...path]
90
151
  });
91
152
  return;
153
+ case "nav":
154
+ setOpenNav(true);
155
+ return;
92
156
  default:
93
157
  return;
94
158
  }
95
159
  };
96
160
  const Toolbar = () => {
161
+ const btnProps = handleLinkType(true, true);
97
162
  return !readOnly ? /*#__PURE__*/_jsxs("div", {
98
163
  className: "element-toolbar hr",
99
164
  style: {
100
165
  width: "max-content",
101
- top: "-38px"
166
+ top: "-38px",
167
+ alignItems: "center",
168
+ cursor: "pointer"
102
169
  },
103
170
  children: [/*#__PURE__*/_jsx(Tooltip, {
104
171
  title: "Settings",
@@ -108,10 +175,21 @@ const EditorButton = props => {
108
175
  children: /*#__PURE__*/_jsx(SettingsIcon, {})
109
176
  })
110
177
  }), /*#__PURE__*/_jsx(Tooltip, {
111
- title: "Open Link",
178
+ title: "Nav Settings",
112
179
  arrow: true,
113
180
  children: /*#__PURE__*/_jsx(IconButton, {
114
- onClick: onMenuClick("open"),
181
+ onClick: onMenuClick("nav"),
182
+ children: /*#__PURE__*/_jsx(LinkIcon, {})
183
+ })
184
+ }), linkType === "page" ? null : /*#__PURE__*/_jsx(Tooltip, {
185
+ title: "Open Link",
186
+ arrow: true,
187
+ children: /*#__PURE__*/_jsx(Box, {
188
+ sx: {
189
+ display: "inline-flex",
190
+ color: "rgba(0, 0, 0, 0.54)"
191
+ },
192
+ ...btnProps,
115
193
  children: /*#__PURE__*/_jsx(OpenInNewIcon, {})
116
194
  })
117
195
  })]
@@ -152,7 +230,7 @@ const EditorButton = props => {
152
230
  },
153
231
  "&:hover": {
154
232
  "& .element-toolbar": {
155
- display: "block"
233
+ display: "flex"
156
234
  }
157
235
  }
158
236
  },
@@ -161,8 +239,8 @@ const EditorButton = props => {
161
239
  position: "relative"
162
240
  },
163
241
  children: [/*#__PURE__*/_jsxs(Box, {
164
- component: "button",
165
242
  sx: {
243
+ textDecoration: "none",
166
244
  background: bgColor || "rgb(30, 75, 122)",
167
245
  borderBlockStyle: "solid",
168
246
  borderColor: borderColor || "transparent",
@@ -187,11 +265,11 @@ const EditorButton = props => {
187
265
  color: `${textColorHover || textColor || "#FFFFFF"}`,
188
266
  background: bgColorHover || bgColor || "rgb(30, 75, 122)",
189
267
  "& .element-toolbar": {
190
- display: "block"
268
+ display: "flex"
191
269
  }
192
270
  }
193
271
  },
194
- onClick: onClick,
272
+ ...buttonProps,
195
273
  children: [BtnIcon && iconPosition === "start" && /*#__PURE__*/_jsx(BtnIcon, {
196
274
  style: {
197
275
  paddingLeft: "4px",
@@ -220,7 +298,14 @@ const EditorButton = props => {
220
298
  onClose: onClose,
221
299
  onDelete: onMenuClick("delete"),
222
300
  customProps: customProps
223
- })]
301
+ }), openNav ? /*#__PURE__*/_jsx(ButtonNavSettings, {
302
+ open: openNav,
303
+ handleClose: () => setOpenNav(false),
304
+ onSave: onSave,
305
+ customProps: customProps,
306
+ element: element,
307
+ editor: editor
308
+ }) : null]
224
309
  });
225
310
  };
226
311
  export default EditorButton;
@@ -24,7 +24,8 @@ const Form = props => {
24
24
  } = props;
25
25
  const {
26
26
  readOnly,
27
- page_id
27
+ page_id,
28
+ onFormSubmit
28
29
  } = customProps;
29
30
  const {
30
31
  buttonProps,
@@ -116,7 +117,10 @@ const Form = props => {
116
117
  if (isValidForm) {
117
118
  alert(isValidForm[0]);
118
119
  } else {
119
- await formSubmit(params, customProps);
120
+ const formRes = await formSubmit(params, customProps);
121
+ if (formRes) {
122
+ onFormSubmit(formRes);
123
+ }
120
124
  }
121
125
  setLoading(false);
122
126
  }
@@ -15,7 +15,7 @@ const MENU_OPTIONS = [{
15
15
  icon: AddTemplateIcon,
16
16
  label: "Add Template"
17
17
  }, {
18
- type: "pageSettings",
18
+ type: "page-settings",
19
19
  icon: PageSettingsButton,
20
20
  label: "Page Settings"
21
21
  }];
@@ -5,7 +5,6 @@ import DownArrowIcon from "../../../assets/svg/DownArrowIcon";
5
5
  import useWindowResize from "../../../hooks/useWindowResize";
6
6
  import { BREAKPOINTS_DEVICES, getBreakPointsValue } from "../../../helper/theme";
7
7
  import { headingMap, sizeMap } from "../../../utils/font";
8
- import { useDebounce } from "use-debounce";
9
8
  import { jsx as _jsx } from "react/jsx-runtime";
10
9
  import { jsxs as _jsxs } from "react/jsx-runtime";
11
10
  const fontSizeOptions = [16, 18, 20, 22, 26, 32, 36, 40, 48, 64, 96, 128];
@@ -21,7 +20,7 @@ function SelectFontSize({
21
20
  const [size] = useWindowResize();
22
21
  const val = activeMark(editor, format);
23
22
  const value = getBreakPointsValue(val, size?.device);
24
- const [deboundedValue] = useDebounce(fontSize, 500);
23
+ const timerRef = useRef();
25
24
  const updateMarkData = newVal => {
26
25
  let upData = {
27
26
  ...getBreakPointsValue(val),
@@ -52,11 +51,6 @@ function SelectFontSize({
52
51
  setFontSize(null);
53
52
  }
54
53
  };
55
- useEffect(() => {
56
- if (deboundedValue) {
57
- onChangeSize(deboundedValue);
58
- }
59
- }, [deboundedValue]);
60
54
  const getSizeVal = () => {
61
55
  try {
62
56
  let size = `${value}`?.indexOf("px") >= 0 ? value : sizeMap[value] || value;
@@ -73,6 +67,14 @@ function SelectFontSize({
73
67
  useEffect(() => {
74
68
  setFontSize(getSizeVal());
75
69
  }, [value]);
70
+ const onChange = e => {
71
+ clearTimeout(timerRef.current);
72
+ const value = e.target.value;
73
+ setFontSize(value);
74
+ timerRef.current = setTimeout(() => {
75
+ onChangeSize(value);
76
+ }, 500);
77
+ };
76
78
  return /*#__PURE__*/_jsxs("div", {
77
79
  ref: containerRef,
78
80
  style: {
@@ -82,7 +84,7 @@ function SelectFontSize({
82
84
  children: [/*#__PURE__*/_jsx(TextField, {
83
85
  sx: classes?.miniFontSizeInput,
84
86
  value: fontSize,
85
- onChange: e => setFontSize(e.target.value),
87
+ onChange: onChange,
86
88
  size: "small"
87
89
  }), /*#__PURE__*/_jsx(IconButton, {
88
90
  onClick: e => {
@@ -10,6 +10,7 @@ import { useEditorSelection } from "../../hooks/useMouseMove";
10
10
  import SectionStyle from "./styles";
11
11
  import { jsx as _jsx } from "react/jsx-runtime";
12
12
  import { jsxs as _jsxs } from "react/jsx-runtime";
13
+ const list_types = ["orderedList", "unorderedList"];
13
14
  const Section = props => {
14
15
  const classes = SectionStyle();
15
16
  const {
@@ -77,7 +78,7 @@ const Section = props => {
77
78
  at: path
78
79
  });
79
80
  };
80
- const needHover = element?.children?.find(f => f.type === "grid") ? "needHover" : "";
81
+ const needHover = element?.children?.find(f => f.type === "grid" && !list_types.includes(element.type)) ? "needHover" : "";
81
82
  const sectionBgImage = sectionBackgroundImage && sectionBackgroundImage !== "none" ? {
82
83
  backgroundImage: `url(${sectionBackgroundImage})`
83
84
  } : {};
@@ -50,15 +50,19 @@ const buttonStyle = [{
50
50
  key: "borderColor",
51
51
  type: "color"
52
52
  }]
53
- }, {
54
- tab: "Link",
55
- value: "link",
56
- fields: [{
57
- label: "Button Link",
58
- key: "buttonLink",
59
- type: "buttonLink"
60
- }]
61
- }, {
53
+ },
54
+ // {
55
+ // tab: "Link",
56
+ // value: "link",
57
+ // fields: [
58
+ // {
59
+ // label: "Button Link",
60
+ // key: "buttonLink",
61
+ // type: "buttonLink",
62
+ // },
63
+ // ],
64
+ // },
65
+ {
62
66
  tab: "Banner Spacing",
63
67
  value: "bannerSpacing",
64
68
  fields: [{
@@ -1,5 +1,20 @@
1
1
  import { Transforms, Editor, Element, Node } from "slate";
2
2
  import deserialize from "../helper/deserialize";
3
+ import { decodeAndParseBase64 } from "../utils/helper";
4
+ const avoidDefaultInsert = ["table", "grid"];
5
+ const loopChildren = (children = [], defaultInsert) => {
6
+ if (!children?.length) {
7
+ return defaultInsert;
8
+ }
9
+ for (let child of children) {
10
+ if (avoidDefaultInsert.includes(child?.type)) {
11
+ defaultInsert = false;
12
+ break;
13
+ }
14
+ defaultInsert = loopChildren(child.children, defaultInsert);
15
+ }
16
+ return defaultInsert;
17
+ };
3
18
  const getCurrentElement = editor => {
4
19
  try {
5
20
  if (editor.selection) {
@@ -52,11 +67,17 @@ const withHtml = editor => {
52
67
  const [tableNode] = Editor.nodes(editor, {
53
68
  match: n => !Editor.isEditor(n) && Element.isElement(n) && n.type === "table"
54
69
  });
55
- // do not paste table cell inside table cell
56
- // only plain text for internal paste
57
70
  if (tableNode && tableNode[0]) {
58
- const text = data?.getData("text/plain");
59
- Transforms.insertText(editor, text);
71
+ const decoded = decodeAndParseBase64(slateHTML);
72
+ const defaultInsert = loopChildren(decoded, true);
73
+ if (defaultInsert) {
74
+ insertData(data);
75
+ } else {
76
+ // do not paste table, grid inside table cell
77
+ // only plain text for internal paste
78
+ const text = data?.getData("text/plain");
79
+ Transforms.insertText(editor, text);
80
+ }
60
81
  } else {
61
82
  insertData(data);
62
83
  }
@@ -1,10 +1,35 @@
1
1
  import { Editor, Range, Point, Element, Transforms, Node } from "slate";
2
+ import { TableUtil, createTableCell } from "../utils/table";
2
3
  const NON_DELETABLE_BLOCKS = ["table-cell", "carousel-item"];
3
4
  const withTable = editor => {
4
5
  const {
5
6
  deleteBackward,
6
- deleteForward
7
+ deleteForward,
8
+ delete: slateDelete
7
9
  } = editor;
10
+ editor.delete = arg => {
11
+ if (arg.reverse) {
12
+ const table = new TableUtil(editor);
13
+ const cellsSelected = table.isCellSelected(editor.selection);
14
+ if (cellsSelected) {
15
+ cellsSelected.forEach(cellPath => {
16
+ Transforms.removeNodes(editor, {
17
+ at: cellPath
18
+ });
19
+ Transforms.insertNodes(editor, createTableCell(""), {
20
+ at: cellPath
21
+ });
22
+ });
23
+ Transforms.deselect(editor, {
24
+ at: editor.selection
25
+ });
26
+ } else {
27
+ slateDelete(arg);
28
+ }
29
+ } else {
30
+ slateDelete(arg);
31
+ }
32
+ };
8
33
  editor.deleteBackward = unit => {
9
34
  const {
10
35
  selection
@@ -9,7 +9,7 @@ export const formSubmit = async (formData, props) => {
9
9
  body: JSON.stringify(formData)
10
10
  });
11
11
  const result = await response.json();
12
- return result.data;
12
+ return result?.data?.data;
13
13
  } catch (err) {
14
14
  console.log(err);
15
15
  return err;
@@ -1,7 +1,6 @@
1
- import { Transforms } from "slate";
2
1
  import default_grid from "../Elements/Grid/templates/default_grid";
3
- import insertNewLine from "./insertNewLine";
4
2
  import { gridItem } from "./gridItem";
3
+ import { customInsertNode } from "./helper";
5
4
  export const insertPlainGrid = count => {
6
5
  const size = 12 / count;
7
6
  const items = Array.from(Array(count).keys()).map(() => gridItem({
@@ -38,11 +37,10 @@ export const insertGrid = (editor, item, path) => {
38
37
  const {
39
38
  selection
40
39
  } = editor;
41
- Transforms.insertNodes(editor, grid, {
40
+ customInsertNode(editor, grid, {
42
41
  at: path || selection.focus.path,
43
42
  select: true
44
43
  });
45
- insertNewLine(editor);
46
44
  } catch (err) {
47
45
  console.log(err);
48
46
  }
@@ -1,5 +1,6 @@
1
1
  import { Editor, Node, Transforms, Element } from "slate";
2
2
  import { ReactEditor } from "slate-react";
3
+ import insertNewLine from "./insertNewLine";
3
4
  export const windowVar = {};
4
5
  export const formatDate = (date, format = "MM/DD/YYYY") => {
5
6
  if (!date) return "";
@@ -149,4 +150,56 @@ export const isListItem = editor => {
149
150
  match: n => !Editor.isEditor(n) && Element.isElement(n) && format.indexOf(n.type) > -1
150
151
  });
151
152
  return node;
153
+ };
154
+ const getNode = (editor, path) => {
155
+ try {
156
+ return Node.get(editor, path);
157
+ } catch (err) {
158
+ return;
159
+ }
160
+ };
161
+ export const customInsertNode = (editor, insertNode, defaultInsertOptions = {}) => {
162
+ const [parent, parentPath] = Editor.parent(editor, editor.selection.focus.path);
163
+ const isListItem = parent?.type === "list-item" || parent?.type === "check-list-item";
164
+ let newParentPath;
165
+ if (isListItem) {
166
+ const lastPathIndex = parentPath.length - 1;
167
+ const otherPaths = parentPath.slice(0, lastPathIndex);
168
+ const nextChildrenPath = parentPath[lastPathIndex] + 1;
169
+ newParentPath = [...otherPaths, nextChildrenPath];
170
+ const haveElem = getNode(editor, newParentPath);
171
+ if (haveElem) {
172
+ Transforms.splitNodes(editor, {
173
+ at: newParentPath
174
+ });
175
+ }
176
+ const {
177
+ anchor,
178
+ focus
179
+ } = editor.selection;
180
+
181
+ // if editor has selection, e.g /table, /grid is selected, delete that selection
182
+ if (focus.offset > anchor.offset) {
183
+ Transforms.delete(editor, {
184
+ at: editor.selection
185
+ });
186
+ }
187
+ }
188
+ const insertOptions = {
189
+ ...defaultInsertOptions
190
+ };
191
+ if (isListItem) {
192
+ insertOptions.at = editor.selection.focus;
193
+ }
194
+ Transforms.insertNodes(editor, insertNode, insertOptions);
195
+ insertNewLine(editor);
196
+ };
197
+ export const decodeAndParseBase64 = encodedString => {
198
+ // Decode the Base64-encoded string
199
+ const decodedString = atob(encodedString);
200
+
201
+ // URL-decode the decoded string
202
+ const decodedURLString = decodeURIComponent(decodedString);
203
+ const jsonData = JSON.parse(decodedURLString);
204
+ return jsonData;
152
205
  };
@@ -1,6 +1,6 @@
1
1
  import { Transforms, Editor, Range, Element, Path, Node } from "slate";
2
2
  import { ReactEditor } from "slate-react";
3
- import insertNewLine from "./insertNewLine";
3
+ import { customInsertNode } from "./helper";
4
4
  const prefixKey = (obj, pk = "") => {
5
5
  return Object.keys(obj).reduce((a, b) => {
6
6
  a[`${pk}${b}`] = obj[b];
@@ -36,11 +36,7 @@ export class TableUtil {
36
36
  length: columns
37
37
  }, () => ""));
38
38
  const newTable = createTableNode(cellText, rows, columns);
39
- Transforms.insertNodes(this.editor, newTable, {
40
- // to insert in current line
41
- // at: this.editor.selection.anchor.path,
42
- });
43
- insertNewLine(this.editor);
39
+ customInsertNode(this.editor, newTable);
44
40
  };
45
41
  removeTable = () => {
46
42
  Transforms.removeNodes(this.editor, {
@@ -318,7 +314,7 @@ export class TableUtil {
318
314
  };
319
315
  isCellSelected = selection => {
320
316
  try {
321
- if (!selection || Range.isCollapsed(selection) || Editor.string(this.editor, selection) === "") {
317
+ if (!selection) {
322
318
  return false;
323
319
  }
324
320
  const [tableNode] = Editor.nodes(this.editor, {
@@ -332,7 +328,7 @@ export class TableUtil {
332
328
  anchor,
333
329
  focus
334
330
  } = this.editor.selection || {};
335
- if (tableNode && tableNode[0] && tableCellPath && tableCellPath[1] && focus?.path) {
331
+ if (tableNode && tableNode[0] && focus?.path) {
336
332
  let startCell = anchor?.path;
337
333
  let endCell = focus?.path;
338
334
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flozy/editor",
3
- "version": "3.1.0",
3
+ "version": "3.1.2",
4
4
  "description": "An Editor for flozy app brain",
5
5
  "files": [
6
6
  "dist"