@flozy/editor 3.1.0 → 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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"