@flozy/editor 3.1.0 → 3.1.1

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,89 @@ 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
+ props.href = `/${url}`.replace("home", "");
111
+ if (openInNewTab) {
112
+ props.target = "_blank";
113
+ }
114
+ break;
115
+ case "email":
116
+ props.component = "a";
117
+ props.href = `mailto:${url}`;
118
+ break;
119
+ case "phone":
120
+ props.component = "a";
121
+ props.href = `tel:${url}`;
122
+ break;
123
+ case "scrollTopOrBottom":
124
+ props.component = "button";
125
+ props.onClick = () => {
126
+ const scrollEle = document.getElementById("slate-wrapper-scroll-container");
127
+ if (scrollEle) {
128
+ if (url === "top") {
129
+ // top of the page
130
+ scrollEle.scrollTo(0, 0);
131
+ } else if (url === "bottom") {
132
+ // bottom of the page
133
+ scrollEle.scrollTo(0, scrollEle.scrollHeight);
134
+ }
135
+ }
136
+ };
137
+ break;
76
138
  }
139
+ return props;
77
140
  };
141
+ const buttonProps = handleLinkType(readOnly, openInNewTab);
78
142
  const onMenuClick = val => () => {
79
143
  switch (val) {
80
- case "open":
81
- const refUrl = url ? url.includes("http") ? url : `//${url}` : "Link";
82
- window.open(refUrl, "_blank").focus();
83
- return;
84
144
  case "edit":
85
145
  setEdit(true);
86
146
  return;
@@ -89,16 +149,22 @@ const EditorButton = props => {
89
149
  at: [...path]
90
150
  });
91
151
  return;
152
+ case "nav":
153
+ setOpenNav(true);
154
+ return;
92
155
  default:
93
156
  return;
94
157
  }
95
158
  };
96
159
  const Toolbar = () => {
160
+ const btnProps = handleLinkType(true, true);
97
161
  return !readOnly ? /*#__PURE__*/_jsxs("div", {
98
162
  className: "element-toolbar hr",
99
163
  style: {
100
164
  width: "max-content",
101
- top: "-38px"
165
+ top: "-38px",
166
+ alignItems: "center",
167
+ cursor: "pointer"
102
168
  },
103
169
  children: [/*#__PURE__*/_jsx(Tooltip, {
104
170
  title: "Settings",
@@ -108,10 +174,21 @@ const EditorButton = props => {
108
174
  children: /*#__PURE__*/_jsx(SettingsIcon, {})
109
175
  })
110
176
  }), /*#__PURE__*/_jsx(Tooltip, {
111
- title: "Open Link",
177
+ title: "Nav Settings",
112
178
  arrow: true,
113
179
  children: /*#__PURE__*/_jsx(IconButton, {
114
- onClick: onMenuClick("open"),
180
+ onClick: onMenuClick("nav"),
181
+ children: /*#__PURE__*/_jsx(LinkIcon, {})
182
+ })
183
+ }), /*#__PURE__*/_jsx(Tooltip, {
184
+ title: "Open Link",
185
+ arrow: true,
186
+ children: /*#__PURE__*/_jsx(Box, {
187
+ sx: {
188
+ display: "inline-flex",
189
+ color: "rgba(0, 0, 0, 0.54)"
190
+ },
191
+ ...btnProps,
115
192
  children: /*#__PURE__*/_jsx(OpenInNewIcon, {})
116
193
  })
117
194
  })]
@@ -152,7 +229,7 @@ const EditorButton = props => {
152
229
  },
153
230
  "&:hover": {
154
231
  "& .element-toolbar": {
155
- display: "block"
232
+ display: "flex"
156
233
  }
157
234
  }
158
235
  },
@@ -161,8 +238,8 @@ const EditorButton = props => {
161
238
  position: "relative"
162
239
  },
163
240
  children: [/*#__PURE__*/_jsxs(Box, {
164
- component: "button",
165
241
  sx: {
242
+ textDecoration: "none",
166
243
  background: bgColor || "rgb(30, 75, 122)",
167
244
  borderBlockStyle: "solid",
168
245
  borderColor: borderColor || "transparent",
@@ -187,11 +264,11 @@ const EditorButton = props => {
187
264
  color: `${textColorHover || textColor || "#FFFFFF"}`,
188
265
  background: bgColorHover || bgColor || "rgb(30, 75, 122)",
189
266
  "& .element-toolbar": {
190
- display: "block"
267
+ display: "flex"
191
268
  }
192
269
  }
193
270
  },
194
- onClick: onClick,
271
+ ...buttonProps,
195
272
  children: [BtnIcon && iconPosition === "start" && /*#__PURE__*/_jsx(BtnIcon, {
196
273
  style: {
197
274
  paddingLeft: "4px",
@@ -220,7 +297,14 @@ const EditorButton = props => {
220
297
  onClose: onClose,
221
298
  onDelete: onMenuClick("delete"),
222
299
  customProps: customProps
223
- })]
300
+ }), openNav ? /*#__PURE__*/_jsx(ButtonNavSettings, {
301
+ open: openNav,
302
+ handleClose: () => setOpenNav(false),
303
+ onSave: onSave,
304
+ customProps: customProps,
305
+ element: element,
306
+ editor: editor
307
+ }) : null]
224
308
  });
225
309
  };
226
310
  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
  }];
@@ -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.1",
4
4
  "description": "An Editor for flozy app brain",
5
5
  "files": [
6
6
  "dist"