@flozy/editor 3.2.8 → 3.3.0

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.
@@ -99,6 +99,7 @@ const EditorButton = props => {
99
99
  };
100
100
  const Toolbar = () => {
101
101
  const btnProps = handleLinkType(refURl, linkType, true, true, handleTrigger);
102
+ const hideOpenLink = linkType === "page" || !linkType;
102
103
  return !readOnly ? /*#__PURE__*/_jsxs("div", {
103
104
  className: "element-toolbar hr",
104
105
  style: {
@@ -121,7 +122,7 @@ const EditorButton = props => {
121
122
  onClick: onMenuClick("nav"),
122
123
  children: /*#__PURE__*/_jsx(LinkIcon, {})
123
124
  })
124
- }), linkType === "page" ? null : /*#__PURE__*/_jsx(Tooltip, {
125
+ }), hideOpenLink ? null : /*#__PURE__*/_jsx(Tooltip, {
125
126
  title: "Open Link",
126
127
  arrow: true,
127
128
  children: /*#__PURE__*/_jsx(Box, {
@@ -1,4 +1,6 @@
1
1
  import React, { useEffect, useRef } from "react";
2
+ import sanitizeHtml from "sanitize-html";
3
+ import { allowedDomains } from "../../utils/helper";
2
4
 
3
5
  // const sanitize = (input) => {
4
6
  // const doc = new DOMParser().parseFromString(input, "text/html");
@@ -25,7 +27,14 @@ const Code = props => {
25
27
  } = element;
26
28
  useEffect(() => {
27
29
  if (codeRef?.current) {
28
- const slotHtml = document.createRange().createContextualFragment(embedData); // Create a 'tiny' document and parse the html string
30
+ const clean = sanitizeHtml(embedData, {
31
+ allowedTags: false,
32
+ // Allow all tags
33
+ allowedAttributes: false,
34
+ // Allow all attributes
35
+ allowedScriptDomains: allowedDomains
36
+ });
37
+ const slotHtml = document.createRange().createContextualFragment(clean); // Create a 'tiny' document and parse the html string
29
38
  codeRef.current.innerHTML = ""; // Clear the container
30
39
  codeRef.current.appendChild(slotHtml);
31
40
  }
@@ -40,7 +40,8 @@ const Form = props => {
40
40
  formTitle,
41
41
  textSize,
42
42
  fontFamily,
43
- textAlign
43
+ textAlign,
44
+ alignment
44
45
  } = element;
45
46
  const btnR = buttonProps?.borderRadius || {};
46
47
  const btnSpacing = buttonProps?.bannerSpacing || {};
@@ -218,7 +219,8 @@ const Form = props => {
218
219
  contentEditable: false,
219
220
  style: {
220
221
  top: "-42px",
221
- left: "0px"
222
+ left: "0px",
223
+ textAlign: "left"
222
224
  },
223
225
  children: [/*#__PURE__*/_jsx(Tooltip, {
224
226
  title: "Form Settings",
@@ -260,7 +262,8 @@ const Form = props => {
260
262
  className: "form-wrapper element-root",
261
263
  style: {
262
264
  border: !readOnly ? "none" : "none",
263
- padding: "10px"
265
+ padding: "10px",
266
+ width: "100%"
264
267
  },
265
268
  onMouseOver: onMouseOver,
266
269
  onMouseLeave: onMouseLeave,
@@ -290,6 +293,9 @@ const Form = props => {
290
293
  children: /*#__PURE__*/_jsxs(Grid, {
291
294
  container: true,
292
295
  spacing: 2,
296
+ sx: {
297
+ justifyContent: alignment?.horizantal || "start"
298
+ },
293
299
  children: [/*#__PURE__*/_jsx("legend", {
294
300
  style: {
295
301
  fontSize: `${textSize}px` || "inherit",
@@ -8,6 +8,7 @@ import "./Signature.css";
8
8
  import { DrawSignature, PencilIcon, TypeSignature, UploadSignature } from "../../common/EditorIcons";
9
9
  import useCommonStyle from "../../commonStyle";
10
10
  import { useEditorContext } from "../../hooks/useMouseMove";
11
+ import { validationMethods } from "../Form/FormElements/validations";
11
12
  import { jsx as _jsx } from "react/jsx-runtime";
12
13
  import { jsxs as _jsxs } from "react/jsx-runtime";
13
14
  import { Fragment as _Fragment } from "react/jsx-runtime";
@@ -25,7 +26,8 @@ const SignaturePopup = props => {
25
26
  } = props;
26
27
  const {
27
28
  readOnly,
28
- metadata
29
+ metadata,
30
+ isSignerDetailsRequired = false
29
31
  } = customProps;
30
32
  const [open, setOpen] = useState(false);
31
33
  const [tab, setTab] = useState(0);
@@ -33,7 +35,9 @@ const SignaturePopup = props => {
33
35
  const [signedData, setSignedData] = useState({
34
36
  signedOn: new Date(),
35
37
  signature: "",
36
- signedText: ""
38
+ signedText: "",
39
+ signedBy: "",
40
+ signedByEmail: ""
37
41
  });
38
42
  const [brush, setBrush] = useState({
39
43
  size: 1,
@@ -49,14 +53,22 @@ const SignaturePopup = props => {
49
53
  await customProps?.services("workFlowAction", {
50
54
  resource_id: customProps?.page_id
51
55
  });
52
- onSave(signedData);
53
- // manual delay
54
- setTimeout(() => {
55
- if (metadata && metadata?.actionHandler) {
56
- metadata?.actionHandler("signed", signedData);
57
- }
58
- handleClose();
59
- }, 0);
56
+ let isValidEmail = "";
57
+ if (isSignerDetailsRequired && signedData?.signedByEmail !== "") {
58
+ isValidEmail = validationMethods?.isEmail(signedData?.signedByEmail);
59
+ }
60
+ if (isValidEmail === "Enter valid email address") {
61
+ alert(isValidEmail);
62
+ } else {
63
+ onSave(signedData);
64
+ // manual delay
65
+ setTimeout(() => {
66
+ if (metadata && metadata?.actionHandler) {
67
+ metadata?.actionHandler("signed", signedData);
68
+ }
69
+ handleClose();
70
+ }, 0);
71
+ }
60
72
  };
61
73
  const handleClear = () => {
62
74
  onClear();
@@ -99,10 +111,15 @@ const SignaturePopup = props => {
99
111
  setSignedData({
100
112
  signedOn: new Date(),
101
113
  signature: "",
102
- signedText: ""
114
+ signedText: "",
115
+ signedBy: "",
116
+ signedByEmail: ""
103
117
  });
104
118
  };
105
- const isEmpty = signedData?.signature === "" && signedData?.signedText === "";
119
+ let isEmpty = signedData?.signature === "" && signedData?.signedText === "";
120
+ if (isSignerDetailsRequired) {
121
+ isEmpty = signedData?.signature === "" && signedData?.signedText === "" || signedData?.signedBy === "" || signedData?.signedByEmail === "";
122
+ }
106
123
  return /*#__PURE__*/_jsxs(_Fragment, {
107
124
  children: [/*#__PURE__*/_jsx("div", {
108
125
  className: `signature-btn-container`,
@@ -165,15 +165,13 @@ export const toolbarGroups = [[{
165
165
  format: "video",
166
166
  type: "embed",
167
167
  group: "elements"
168
- },
169
- // {
170
- // id: 49,
171
- // lbT: "embedScript",
172
- // format: "embedScript",
173
- // type: "embedScript",
174
- // group: "elements",
175
- // },
176
- {
168
+ }, {
169
+ id: 49,
170
+ lbT: "embedScript",
171
+ format: "embedScript",
172
+ type: "embedScript",
173
+ group: "elements"
174
+ }, {
177
175
  id: 45,
178
176
  format: "topbanner",
179
177
  type: "topbanner",
@@ -103,7 +103,7 @@ export const SelectPage = props => {
103
103
  };
104
104
  export const Trigger = props => {
105
105
  return /*#__PURE__*/_jsx(Typography, {
106
- variant: "h6",
106
+ variant: "subtitle1",
107
107
  gutterBottom: true,
108
108
  children: "Choosing this will trigger the next step"
109
109
  });
@@ -1,17 +1,20 @@
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 LinkSettingsStyles from './style';
11
- import { getNavOptions } from './navOptions';
12
- import { ScrollTopBottom, SelectPage, TextInput, Trigger } from './NavComponents';
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 { Box, FormControl, FormControlLabel, Grid, MenuItem, Radio, RadioGroup, Select, Typography } from "@mui/material";
9
+ import { useState } from "react";
10
+ import LinkSettingsStyles from "./style";
11
+ import { getNavOptions } from "./navOptions";
12
+ import { ScrollTopBottom, SelectPage, TextInput, Trigger } from "./NavComponents";
13
+ import SwipeableDrawer from "../SwipeableDrawer";
14
+ import { useEditorContext } from "../../hooks/useMouseMove";
13
15
  import { jsx as _jsx } from "react/jsx-runtime";
14
16
  import { jsxs as _jsxs } from "react/jsx-runtime";
17
+ import { Fragment as _Fragment } from "react/jsx-runtime";
15
18
  const MAP_COMPONENT = {
16
19
  webAddress: TextInput,
17
20
  email: TextInput,
@@ -20,6 +23,12 @@ const MAP_COMPONENT = {
20
23
  scrollTopOrBottom: ScrollTopBottom,
21
24
  page: SelectPage
22
25
  };
26
+ function getNav(navType, navOptions) {
27
+ return navType ? navOptions.find(n => n.value === navType) : {
28
+ label: "None",
29
+ value: ""
30
+ };
31
+ }
23
32
  export default function LinkSettings(props) {
24
33
  const {
25
34
  handleClose,
@@ -27,86 +36,157 @@ export default function LinkSettings(props) {
27
36
  customProps,
28
37
  navType
29
38
  } = props;
39
+ const {
40
+ isMobile
41
+ } = customProps;
30
42
  const navOptions = getNavOptions(customProps.hideTools);
31
43
  const classes = LinkSettingsStyles();
32
- const [nav, setNav] = useState(navType ? navOptions.find(n => n.value === navType) : {
33
- label: "None",
34
- value: ""
35
- });
44
+ const [nav, setNav] = useState(getNav(navType, navOptions));
36
45
  const [navValue, setNavValue] = useState(props?.navValue || "");
37
46
  const [openInNewTab, setOpenInNewTab] = useState(props.openInNewTab || false);
38
47
  const NavSettings = MAP_COMPONENT[nav?.value];
39
48
  const onChange = value => {
40
49
  setNavValue(value);
41
50
  };
42
- return /*#__PURE__*/_jsxs(Dialog, {
43
- onClose: handleClose,
44
- open: true,
45
- sx: classes.dialogContainer,
46
- fullWidth: true,
47
- maxWidth: "sm",
48
- children: [/*#__PURE__*/_jsx(DialogTitle, {
49
- children: "What do you want to link to?"
50
- }), /*#__PURE__*/_jsx(IconButton, {
51
- "aria-label": "close",
52
- onClick: handleClose,
53
- sx: classes.closeIcon,
54
- children: /*#__PURE__*/_jsx(CloseIcon, {})
55
- }), /*#__PURE__*/_jsx(DialogContent, {
56
- dividers: true,
57
- children: /*#__PURE__*/_jsxs(Grid, {
58
- container: true,
59
- spacing: 2,
60
- children: [/*#__PURE__*/_jsx(Grid, {
61
- item: true,
62
- xs: 6,
63
- sx: classes.gridDivider,
64
- children: /*#__PURE__*/_jsx(FormControl, {
65
- children: /*#__PURE__*/_jsx(RadioGroup, {
66
- value: nav?.value,
67
- children: navOptions?.map((navOption, i) => {
68
- return /*#__PURE__*/_jsx(FormControlLabel, {
69
- value: navOption.value,
70
- control: /*#__PURE__*/_jsx(Radio, {}),
71
- label: navOption.label,
72
- onChange: () => {
73
- setNav(navOption);
74
- setNavValue("");
75
- }
76
- }, i);
77
- })
51
+ const onSubmit = () => {
52
+ onSave({
53
+ linkType: nav?.value,
54
+ navValue,
55
+ openInNewTab
56
+ });
57
+ };
58
+ if (isMobile) {
59
+ return /*#__PURE__*/_jsxs(SwipeableDrawer, {
60
+ onClose: handleClose,
61
+ children: [/*#__PURE__*/_jsx(Typography, {
62
+ variant: "subtitle1",
63
+ gutterBottom: true,
64
+ sx: {
65
+ fontWeight: 600
66
+ },
67
+ children: "What do you want to link to?"
68
+ }), /*#__PURE__*/_jsx(Select, {
69
+ size: "small",
70
+ fullWidth: true,
71
+ value: nav?.value,
72
+ onChange: e => {
73
+ const {
74
+ value
75
+ } = e.target;
76
+ const selected = getNav(value, navOptions);
77
+ setNav(selected);
78
+ setNavValue("");
79
+ },
80
+ displayEmpty: true,
81
+ children: navOptions.map((navOption, i) => {
82
+ return /*#__PURE__*/_jsx(MenuItem, {
83
+ value: navOption.value,
84
+ children: /*#__PURE__*/_jsx(Typography, {
85
+ variant: "body2",
86
+ children: navOption.label
78
87
  })
79
- })
80
- }), /*#__PURE__*/_jsx(Grid, {
81
- item: true,
82
- xs: 6,
83
- children: NavSettings && /*#__PURE__*/_jsx(NavSettings, {
84
- placeholder: nav?.placeholder,
85
- nav: nav,
86
- onChange: onChange,
87
- value: navValue,
88
- openInNewTab: openInNewTab,
89
- onNewTabChange: () => setOpenInNewTab(prev => !prev),
90
- services: customProps.services
91
- })
92
- })]
93
- })
94
- }), /*#__PURE__*/_jsxs(DialogActions, {
95
- children: [/*#__PURE__*/_jsx(Button, {
96
- onClick: handleClose,
97
- sx: classes.closeBtn,
98
- children: "Cancel"
99
- }), /*#__PURE__*/_jsx(Button, {
100
- onClick: () => {
101
- onSave({
102
- linkType: nav?.value,
103
- navValue,
104
- openInNewTab
105
- });
88
+ }, i);
89
+ })
90
+ }), /*#__PURE__*/_jsx(Box, {
91
+ sx: {
92
+ paddingTop: "14px"
106
93
  },
107
- sx: classes.saveBtn,
108
- children: "Save"
94
+ children: NavSettings && /*#__PURE__*/_jsx(NavSettings, {
95
+ placeholder: nav?.placeholder,
96
+ nav: nav,
97
+ onChange: onChange,
98
+ value: navValue,
99
+ openInNewTab: openInNewTab,
100
+ onNewTabChange: () => setOpenInNewTab(prev => !prev),
101
+ services: customProps.services
102
+ })
103
+ }), /*#__PURE__*/_jsx(Box, {
104
+ component: "div",
105
+ sx: classes.mobileActionBtns,
106
+ children: /*#__PURE__*/_jsx(ActionsButtons, {
107
+ classes: classes,
108
+ onCancel: handleClose,
109
+ onSave: onSubmit
110
+ })
111
+ })]
112
+ });
113
+ } else {
114
+ return /*#__PURE__*/_jsxs(Dialog, {
115
+ onClose: handleClose,
116
+ open: true,
117
+ sx: classes.dialogContainer,
118
+ fullWidth: true,
119
+ maxWidth: "sm",
120
+ children: [/*#__PURE__*/_jsx(DialogTitle, {
121
+ children: "What do you want to link to?"
122
+ }), /*#__PURE__*/_jsx(IconButton, {
123
+ "aria-label": "close",
124
+ onClick: handleClose,
125
+ sx: classes.closeIcon,
126
+ children: /*#__PURE__*/_jsx(CloseIcon, {})
127
+ }), /*#__PURE__*/_jsx(DialogContent, {
128
+ dividers: true,
129
+ children: /*#__PURE__*/_jsxs(Grid, {
130
+ container: true,
131
+ spacing: 2,
132
+ children: [/*#__PURE__*/_jsx(Grid, {
133
+ item: true,
134
+ xs: 6,
135
+ sx: classes.gridDivider,
136
+ children: /*#__PURE__*/_jsx(FormControl, {
137
+ children: /*#__PURE__*/_jsx(RadioGroup, {
138
+ value: nav?.value,
139
+ children: navOptions?.map((navOption, i) => {
140
+ return /*#__PURE__*/_jsx(FormControlLabel, {
141
+ value: navOption.value,
142
+ control: /*#__PURE__*/_jsx(Radio, {}),
143
+ label: navOption.label,
144
+ onChange: () => {
145
+ setNav(navOption);
146
+ setNavValue("");
147
+ }
148
+ }, i);
149
+ })
150
+ })
151
+ })
152
+ }), /*#__PURE__*/_jsx(Grid, {
153
+ item: true,
154
+ xs: 6,
155
+ children: NavSettings && /*#__PURE__*/_jsx(NavSettings, {
156
+ placeholder: nav?.placeholder,
157
+ nav: nav,
158
+ onChange: onChange,
159
+ value: navValue,
160
+ openInNewTab: openInNewTab,
161
+ onNewTabChange: () => setOpenInNewTab(prev => !prev),
162
+ services: customProps.services
163
+ })
164
+ })]
165
+ })
166
+ }), /*#__PURE__*/_jsx(DialogActions, {
167
+ children: /*#__PURE__*/_jsx(ActionsButtons, {
168
+ classes: classes,
169
+ onCancel: handleClose,
170
+ onSave: onSubmit
171
+ })
109
172
  })]
173
+ });
174
+ }
175
+ }
176
+ function ActionsButtons({
177
+ classes,
178
+ onCancel,
179
+ onSave
180
+ }) {
181
+ return /*#__PURE__*/_jsxs(_Fragment, {
182
+ children: [/*#__PURE__*/_jsx(Button, {
183
+ onClick: onCancel,
184
+ sx: classes.closeBtn,
185
+ children: "Cancel"
186
+ }), /*#__PURE__*/_jsx(Button, {
187
+ onClick: onSave,
188
+ sx: classes.saveBtn,
189
+ children: "Save"
110
190
  })]
111
191
  });
112
192
  }
@@ -50,6 +50,14 @@ const ButtonNavSettingsStyles = () => ({
50
50
  },
51
51
  gridDivider: {
52
52
  borderRight: "1px solid rgba(0, 0, 0, 0.12)"
53
+ },
54
+ mobileActionBtns: {
55
+ display: "flex",
56
+ justifyContent: "flex-end",
57
+ gap: "8px",
58
+ paddingTop: "14px",
59
+ marginTop: "14px",
60
+ borderTop: theme => `1px solid ${theme.palette.grey[300]}`
53
61
  }
54
62
  });
55
63
  export default ButtonNavSettingsStyles;
@@ -0,0 +1,34 @@
1
+ import { Box, SwipeableDrawer, Typography } from "@mui/material";
2
+ import DrawerStyles from "./style";
3
+ import { grey } from "@mui/material/colors";
4
+ import { styled } from "@mui/material/styles";
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ import { jsxs as _jsxs } from "react/jsx-runtime";
7
+ const Puller = styled("div")(({
8
+ theme
9
+ }) => ({
10
+ width: 30,
11
+ height: 6,
12
+ backgroundColor: theme?.palette?.mode === "light" ? grey[300] : grey[900],
13
+ borderRadius: 3,
14
+ margin: "auto",
15
+ marginTop: "14px"
16
+ }));
17
+ function SwipeableDrawerComponent({
18
+ open,
19
+ onClose,
20
+ children
21
+ }) {
22
+ const classes = DrawerStyles();
23
+ return /*#__PURE__*/_jsxs(SwipeableDrawer, {
24
+ anchor: "bottom",
25
+ open: true,
26
+ onClose: onClose,
27
+ sx: classes.drawerContainer,
28
+ children: [/*#__PURE__*/_jsx(Puller, {}), /*#__PURE__*/_jsx(Box, {
29
+ sx: classes.childContainer,
30
+ children: children
31
+ })]
32
+ });
33
+ }
34
+ export default SwipeableDrawerComponent;
@@ -0,0 +1,12 @@
1
+ const Styles = () => ({
2
+ drawerContainer: {
3
+ "& .MuiDrawer-paper": {
4
+ borderTopLeftRadius: 8,
5
+ borderTopRightRadius: 8
6
+ }
7
+ },
8
+ childContainer: {
9
+ padding: "20px"
10
+ }
11
+ });
12
+ export default Styles;
@@ -296,4 +296,5 @@ export const handleLinkType = (url, linkType, readOnly, openInNewTab, onClick =
296
296
  };
297
297
  }
298
298
  return props;
299
- };
299
+ };
300
+ export const allowedDomains = ["youtube.com", "lemcal.com", "facebook.com", "calendly.com"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flozy/editor",
3
- "version": "3.2.8",
3
+ "version": "3.3.0",
4
4
  "description": "An Editor for flozy app brain",
5
5
  "files": [
6
6
  "dist"
@@ -39,6 +39,7 @@
39
39
  "react-scripts": "5.0.1",
40
40
  "react-signature-canvas": "^1.0.6",
41
41
  "react-slick": "^0.29.0",
42
+ "sanitize-html": "^2.13.0",
42
43
  "slate": "^0.94.1",
43
44
  "slate-history": "^0.93.0",
44
45
  "slate-hyperscript": "^0.100.0",
@@ -63,7 +64,7 @@
63
64
  "storybook": "storybook dev -p 6006",
64
65
  "build-storybook": "storybook build",
65
66
  "publish:npm": "rm -rf dist && mkdir dist && babel src/components -d dist --copy-files",
66
- "publish:local": "rm -rf /Users/agmac23/Desktop/surya/sweetpsocial-2.0/client/node_modules/@flozy/editor/dist && babel src/components -d /Users/agmac23/Desktop/surya/sweetpsocial-2.0/client/node_modules/@flozy/editor/dist --copy-files"
67
+ "publish:local": "rm -rf /Users/agmac03/flozy/client/node_modules/@flozy/editor/dist && babel src/components -d /Users/agmac03/flozy/client/node_modules/@flozy/editor/dist --copy-files"
67
68
  },
68
69
  "eslintConfig": {
69
70
  "extends": [