@pushwoosh/smart-components-kit 1.0.5 → 1.0.7
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.
- package/RichMessageEditor/DynamicContentForm.d.ts +7 -0
- package/RichMessageEditor/DynamicContentForm.js +17 -0
- package/RichMessageEditor/EmojiPicker.d.ts +6 -0
- package/RichMessageEditor/EmojiPicker.js +17 -0
- package/RichMessageEditor/RichMessageEditor.d.ts +11 -0
- package/RichMessageEditor/RichMessageEditor.js +133 -0
- package/RichMessageEditor/RichMessageEditor.styled.d.ts +22 -0
- package/RichMessageEditor/RichMessageEditor.styled.js +25 -0
- package/RichMessageEditor/RichMessageViewerContent.d.ts +6 -0
- package/RichMessageEditor/RichMessageViewerContent.js +30 -0
- package/RichMessageEditor/RichMessageViewerString.d.ts +5 -0
- package/RichMessageEditor/RichMessageViewerString.js +13 -0
- package/RichMessageEditor/SlateEditor.utils.d.ts +6 -0
- package/RichMessageEditor/SlateEditor.utils.js +63 -0
- package/RichMessageEditor/helpers.d.ts +11 -0
- package/RichMessageEditor/helpers.js +96 -0
- package/RichMessageEditor/images/EmojiIcon.d.ts +2 -0
- package/RichMessageEditor/images/EmojiIcon.js +13 -0
- package/RichMessageEditor/images/PersonalizationIcon.d.ts +2 -0
- package/RichMessageEditor/images/PersonalizationIcon.js +13 -0
- package/RichMessageEditor/index.d.ts +3 -0
- package/RichMessageEditor/index.js +3 -0
- package/RichMessageEditor/types.d.ts +26 -0
- package/RichMessageEditor/types.js +1 -0
- package/RichMessageEditor/withDynamicContent.d.ts +2 -0
- package/RichMessageEditor/withDynamicContent.js +13 -0
- package/index.d.ts +1 -0
- package/index.js +2 -1
- package/package.json +6 -2
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Button } from '@pushwoosh/dumb-components';
|
|
3
|
+
import { Horizontal } from '@pushwoosh/kit-helpers';
|
|
4
|
+
import { DynamicContentPicker } from '../DynamicContentPicker';
|
|
5
|
+
export const NewDynamicContentForm = props => {
|
|
6
|
+
const [dc, setDc] = useState();
|
|
7
|
+
return React.createElement(DynamicContentPicker, {
|
|
8
|
+
dc: dc,
|
|
9
|
+
onChange: setDc
|
|
10
|
+
}, React.createElement(Horizontal, {
|
|
11
|
+
"$justifyContent": "end"
|
|
12
|
+
}, React.createElement(Button, {
|
|
13
|
+
color: "secondary",
|
|
14
|
+
onClick: () => props.onSubmit(dc),
|
|
15
|
+
isDisabled: !dc
|
|
16
|
+
}, "Insert")));
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import Picker from '@emoji-mart/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
async function data() {
|
|
4
|
+
const response = await fetch('https://cdn.jsdelivr.net/npm/@emoji-mart/data');
|
|
5
|
+
return response.json();
|
|
6
|
+
}
|
|
7
|
+
export const EmojiPicker = ({
|
|
8
|
+
onEmojiSelect
|
|
9
|
+
}) => {
|
|
10
|
+
return React.createElement(Picker, {
|
|
11
|
+
data: data,
|
|
12
|
+
onEmojiSelect: emoji => {
|
|
13
|
+
onEmojiSelect(emoji.native);
|
|
14
|
+
},
|
|
15
|
+
theme: "light"
|
|
16
|
+
});
|
|
17
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type FC, type ReactNode } from 'react';
|
|
2
|
+
type RichMessageEditorProps = {
|
|
3
|
+
title: ReactNode;
|
|
4
|
+
initialValue: string;
|
|
5
|
+
onChange: (value: string) => void;
|
|
6
|
+
minHeight?: string;
|
|
7
|
+
canUsePersonalization?: boolean;
|
|
8
|
+
canUseEmoji?: boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare const RichMessageEditor: FC<RichMessageEditorProps>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { isEqual } from 'es-toolkit';
|
|
2
|
+
import React, { useRef, useState } from 'react';
|
|
3
|
+
import { Transforms } from 'slate';
|
|
4
|
+
import { Slate, useSlate } from 'slate-react';
|
|
5
|
+
import { FieldBox } from '@pushwoosh/dumb-components';
|
|
6
|
+
import { Collapse, Horizontal, useClickAway } from '@pushwoosh/kit-helpers';
|
|
7
|
+
import { DynamicContentPicker } from '../DynamicContentPicker';
|
|
8
|
+
import { NewDynamicContentForm } from './DynamicContentForm';
|
|
9
|
+
import { EmojiPicker } from './EmojiPicker';
|
|
10
|
+
import { getDcByPath, content2string, string2content } from './helpers';
|
|
11
|
+
import { EmojiIcon } from './images/EmojiIcon';
|
|
12
|
+
import { PersonalizationIcon } from './images/PersonalizationIcon';
|
|
13
|
+
import { DropContentStyled, ImSpan, EditableStyled } from './RichMessageEditor.styled';
|
|
14
|
+
import { createMKEditor, renderElement, insertDynamicContent } from './SlateEditor.utils';
|
|
15
|
+
const DropContent = ({
|
|
16
|
+
top,
|
|
17
|
+
left,
|
|
18
|
+
children
|
|
19
|
+
}) => {
|
|
20
|
+
return React.createElement(DropContentStyled, {
|
|
21
|
+
style: {
|
|
22
|
+
top,
|
|
23
|
+
left
|
|
24
|
+
}
|
|
25
|
+
}, children);
|
|
26
|
+
};
|
|
27
|
+
const ExistsDynamicContent = ({
|
|
28
|
+
path
|
|
29
|
+
}) => {
|
|
30
|
+
const editor = useSlate();
|
|
31
|
+
const el = getDcByPath(editor.children, path);
|
|
32
|
+
if (!el) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return React.createElement(DynamicContentPicker, {
|
|
36
|
+
dc: el.dc,
|
|
37
|
+
onChange: dc => {
|
|
38
|
+
Transforms.setNodes(editor, {
|
|
39
|
+
dc
|
|
40
|
+
}, {
|
|
41
|
+
at: path
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
export const RichMessageEditor = ({
|
|
47
|
+
title,
|
|
48
|
+
initialValue: initialValueString,
|
|
49
|
+
minHeight,
|
|
50
|
+
canUsePersonalization,
|
|
51
|
+
canUseEmoji,
|
|
52
|
+
onChange
|
|
53
|
+
}) => {
|
|
54
|
+
const rootRef = useRef(null);
|
|
55
|
+
const [initialValue] = useState(() => string2content(initialValueString));
|
|
56
|
+
const [editor] = useState(createMKEditor);
|
|
57
|
+
const [showDrop, setShowDrop] = useState();
|
|
58
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
59
|
+
const onClickDrop = val => {
|
|
60
|
+
const rect = rootRef.current.getBoundingClientRect();
|
|
61
|
+
setShowDrop({
|
|
62
|
+
...val,
|
|
63
|
+
xValue: rect.right + 8,
|
|
64
|
+
yValue: rect.top
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
const onSelectionChange = selection => {
|
|
68
|
+
if (isEqual(selection.anchor, selection.focus)) {
|
|
69
|
+
const {
|
|
70
|
+
path
|
|
71
|
+
} = selection.anchor;
|
|
72
|
+
if (path.length > 2) {
|
|
73
|
+
onClickDrop({
|
|
74
|
+
type: 'personalization',
|
|
75
|
+
path: path.slice(0, -1)
|
|
76
|
+
});
|
|
77
|
+
} else {
|
|
78
|
+
setShowDrop(undefined);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const onFocus = () => {
|
|
83
|
+
setIsFocused(true);
|
|
84
|
+
const {
|
|
85
|
+
selection
|
|
86
|
+
} = editor;
|
|
87
|
+
if (selection) {
|
|
88
|
+
onSelectionChange(selection);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
useClickAway(true, rootRef, () => {
|
|
92
|
+
setShowDrop(undefined);
|
|
93
|
+
setIsFocused(false);
|
|
94
|
+
});
|
|
95
|
+
return React.createElement(Slate, {
|
|
96
|
+
editor: editor,
|
|
97
|
+
initialValue: initialValue,
|
|
98
|
+
onValueChange: value => onChange(content2string(value)),
|
|
99
|
+
onSelectionChange: onSelectionChange
|
|
100
|
+
}, React.createElement(FieldBox, {
|
|
101
|
+
title: title,
|
|
102
|
+
boxRef: rootRef,
|
|
103
|
+
actions: React.createElement(Collapse, {
|
|
104
|
+
expanded: isFocused
|
|
105
|
+
}, React.createElement(Horizontal, {
|
|
106
|
+
"$gap": 8
|
|
107
|
+
}, canUsePersonalization && React.createElement(ImSpan, {
|
|
108
|
+
onClick: () => onClickDrop({
|
|
109
|
+
type: 'personalization'
|
|
110
|
+
})
|
|
111
|
+
}, React.createElement(PersonalizationIcon, null), "Personalization"), canUseEmoji && React.createElement(ImSpan, {
|
|
112
|
+
onClick: () => onClickDrop({
|
|
113
|
+
type: 'emoji'
|
|
114
|
+
})
|
|
115
|
+
}, React.createElement(EmojiIcon, null), "Emoji")))
|
|
116
|
+
}, React.createElement(EditableStyled, {
|
|
117
|
+
"$minHeight": minHeight,
|
|
118
|
+
renderElement: renderElement,
|
|
119
|
+
onFocus: onFocus
|
|
120
|
+
}), showDrop && React.createElement(DropContent, {
|
|
121
|
+
top: showDrop.yValue,
|
|
122
|
+
left: showDrop.xValue
|
|
123
|
+
}, showDrop.type === 'personalization' && (showDrop.path ? React.createElement(ExistsDynamicContent, {
|
|
124
|
+
path: showDrop.path
|
|
125
|
+
}) : React.createElement(NewDynamicContentForm, {
|
|
126
|
+
onSubmit: dc => {
|
|
127
|
+
insertDynamicContent(editor, dc);
|
|
128
|
+
setShowDrop(undefined);
|
|
129
|
+
}
|
|
130
|
+
})), showDrop.type === 'emoji' && React.createElement(EmojiPicker, {
|
|
131
|
+
onEmojiSelect: s => editor.insertText(s)
|
|
132
|
+
}))));
|
|
133
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const ImSpan: import("styled-components").StyledComponent<"span", any, {}, never>;
|
|
2
|
+
export declare const DropContentStyled: import("styled-components").StyledComponent<"div", any, {}, never>;
|
|
3
|
+
export declare const EditableStyled: import("styled-components").StyledComponent<import("react").ForwardRefExoticComponent<{
|
|
4
|
+
decorate?: ((entry: import("slate").NodeEntry) => import("slate").DecoratedRange[]) | undefined;
|
|
5
|
+
onDOMBeforeInput?: ((event: InputEvent) => void) | undefined;
|
|
6
|
+
placeholder?: string | undefined;
|
|
7
|
+
readOnly?: boolean | undefined;
|
|
8
|
+
role?: string | undefined;
|
|
9
|
+
style?: React.CSSProperties | undefined;
|
|
10
|
+
renderElement?: ((props: import("slate-react").RenderElementProps) => import("react").JSX.Element) | undefined;
|
|
11
|
+
renderLeaf?: ((props: import("slate-react").RenderLeafProps) => import("react").JSX.Element) | undefined;
|
|
12
|
+
renderPlaceholder?: ((props: import("slate-react").RenderPlaceholderProps) => import("react").JSX.Element) | undefined;
|
|
13
|
+
scrollSelectionIntoView?: ((editor: import("slate-react").ReactEditor, domRange: Range) => void) | undefined;
|
|
14
|
+
as?: React.ElementType<any> | undefined;
|
|
15
|
+
disableDefaultStyles?: boolean | undefined;
|
|
16
|
+
} & import("react").TextareaHTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>, any, {
|
|
17
|
+
$minHeight?: string;
|
|
18
|
+
}, never>;
|
|
19
|
+
export declare const DynamicContentStyled: import("styled-components").StyledComponent<"span", any, {
|
|
20
|
+
$selected?: boolean;
|
|
21
|
+
}, never>;
|
|
22
|
+
export declare const TimesSpan: import("styled-components").StyledComponent<"span", any, {}, never>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Editable } from 'slate-react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { Color, FontSize, FontWeight, LineHeight, ShapeRadius } from '@pushwoosh/kit-constants';
|
|
4
|
+
export const ImSpan = styled.span.withConfig({
|
|
5
|
+
displayName: "ImSpan",
|
|
6
|
+
componentId: "sc-1pyacsp-0"
|
|
7
|
+
})(["color:", ";position:relative;padding-left:20px;font-size:12px;font-style:normal;font-weight:500;line-height:130%;text-transform:uppercase;cursor:pointer;> svg{position:absolute;left:0;top:-1px;}"], Color.SOFT);
|
|
8
|
+
export const DropContentStyled = styled.div.withConfig({
|
|
9
|
+
displayName: "DropContentStyled",
|
|
10
|
+
componentId: "sc-1pyacsp-1"
|
|
11
|
+
})(["position:fixed;z-index:10;"]);
|
|
12
|
+
export const EditableStyled = styled(Editable).withConfig({
|
|
13
|
+
displayName: "EditableStyled",
|
|
14
|
+
componentId: "sc-1pyacsp-2"
|
|
15
|
+
})(["font-weight:", ";font-size:", ";line-height:", ";padding:8px;color:", ";caret-color:", ";border:1px solid ", ";border-radius:", ";overflow-x:auto;", " &:focus{border-color:", ";outline:none;}& > p{margin:4px 0;&:first-child{margin-top:0;}&:last-child{margin-bottom:0;}}"], FontWeight.REGULAR, FontSize.REGULAR, LineHeight.REGULAR, Color.MAIN, Color.BRIGHT, Color.FORM, ShapeRadius.CONTROL, ({
|
|
16
|
+
$minHeight
|
|
17
|
+
}) => $minHeight && `min-height: ${$minHeight};`, Color.BRIGHT);
|
|
18
|
+
export const DynamicContentStyled = styled.span.withConfig({
|
|
19
|
+
displayName: "DynamicContentStyled",
|
|
20
|
+
componentId: "sc-1pyacsp-3"
|
|
21
|
+
})(["display:inline-block;position:relative;color:white;background-color:", ";border-radius:4px;padding-left:4px;padding-right:18px;margin-left:2px;margin-right:2px;box-shadow:0 0 0 1px ", ";cursor:pointer;"], Color.MAIN, props => props.$selected ? '#114B5057' : Color.MAIN);
|
|
22
|
+
export const TimesSpan = styled.span.withConfig({
|
|
23
|
+
displayName: "TimesSpan",
|
|
24
|
+
componentId: "sc-1pyacsp-4"
|
|
25
|
+
})(["position:absolute;display:inline-block;top:0;right:0;width:20px;height:20px;cursor:pointer;transition:transform 0.2s;&:before,&:after{content:'';position:absolute;background-color:white;top:10px;right:4px;width:9px;height:1px;rotate:45deg;}&:after{rotate:-45deg;}&:hover{&:before,&:after{transform:scale(1.2);}}"]);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* eslint-disable react/no-array-index-key */
|
|
2
|
+
import React, { Fragment } from 'react';
|
|
3
|
+
import { Color } from '@pushwoosh/kit-constants';
|
|
4
|
+
import { isElement } from './helpers';
|
|
5
|
+
export const RichMessageViewerContent = ({
|
|
6
|
+
content,
|
|
7
|
+
lineBreaks
|
|
8
|
+
}) => {
|
|
9
|
+
return content.map((node, index) => {
|
|
10
|
+
let children = node.children.map((child, idx) => {
|
|
11
|
+
if (isElement(child) && child.type === 'dc') {
|
|
12
|
+
return React.createElement("span", {
|
|
13
|
+
key: idx,
|
|
14
|
+
style: {
|
|
15
|
+
backgroundColor: Color.WARNING_LIGHT_HOVER,
|
|
16
|
+
outline: `2px solid ${Color.WARNING_LIGHT_HOVER}`,
|
|
17
|
+
borderRadius: 4
|
|
18
|
+
}
|
|
19
|
+
}, child.dc.tagName);
|
|
20
|
+
}
|
|
21
|
+
return child.text;
|
|
22
|
+
}).join('');
|
|
23
|
+
if (index) {
|
|
24
|
+
children = React.createElement(Fragment, {
|
|
25
|
+
key: index
|
|
26
|
+
}, lineBreaks ? React.createElement("br", null) : ' ', children);
|
|
27
|
+
}
|
|
28
|
+
return children;
|
|
29
|
+
});
|
|
30
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { string2content } from './helpers';
|
|
3
|
+
import { RichMessageViewerContent } from './RichMessageViewerContent';
|
|
4
|
+
export const RichMessageViewerString = ({
|
|
5
|
+
text,
|
|
6
|
+
lineBreaks
|
|
7
|
+
}) => {
|
|
8
|
+
const content = useMemo(() => string2content(text), [text]);
|
|
9
|
+
return React.createElement(RichMessageViewerContent, {
|
|
10
|
+
content: content,
|
|
11
|
+
lineBreaks: lineBreaks
|
|
12
|
+
});
|
|
13
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type CustomTypes } from 'slate';
|
|
3
|
+
import { type DynamicContent } from '../DynamicContentPicker';
|
|
4
|
+
export declare const insertDynamicContent: (editor: CustomTypes["Editor"], dc: DynamicContent) => void;
|
|
5
|
+
export declare const createMKEditor: () => import("./custom-types").CustomEditor;
|
|
6
|
+
export declare function renderElement(props: any): React.JSX.Element;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Transforms, createEditor } from 'slate';
|
|
3
|
+
import { withHistory } from 'slate-history';
|
|
4
|
+
import { withReact, useSelected, useSlateStatic } from 'slate-react';
|
|
5
|
+
import { findPath } from './helpers';
|
|
6
|
+
import { DynamicContentStyled, TimesSpan } from './RichMessageEditor.styled';
|
|
7
|
+
import { withDynamicContent } from './withDynamicContent';
|
|
8
|
+
export const insertDynamicContent = (editor, dc) => {
|
|
9
|
+
const newDC = {
|
|
10
|
+
type: 'dc',
|
|
11
|
+
dc,
|
|
12
|
+
children: [{
|
|
13
|
+
text: ''
|
|
14
|
+
}]
|
|
15
|
+
};
|
|
16
|
+
Transforms.insertNodes(editor, newDC);
|
|
17
|
+
Transforms.move(editor);
|
|
18
|
+
};
|
|
19
|
+
export const createMKEditor = () => withDynamicContent(withReact(withHistory(createEditor())));
|
|
20
|
+
const DynamicContentElementComponent = ({
|
|
21
|
+
attributes,
|
|
22
|
+
element,
|
|
23
|
+
children
|
|
24
|
+
}) => {
|
|
25
|
+
const editor = useSlateStatic();
|
|
26
|
+
const selected = useSelected();
|
|
27
|
+
return React.createElement(DynamicContentStyled, {
|
|
28
|
+
...attributes,
|
|
29
|
+
"$selected": selected,
|
|
30
|
+
contentEditable: false
|
|
31
|
+
}, React.createElement("span", null, element.dc.tagName), children, React.createElement(TimesSpan, {
|
|
32
|
+
onClick: event => {
|
|
33
|
+
event.preventDefault();
|
|
34
|
+
event.stopPropagation();
|
|
35
|
+
const path = findPath(editor.children, element);
|
|
36
|
+
Transforms.delete(editor, {
|
|
37
|
+
at: path
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}));
|
|
41
|
+
};
|
|
42
|
+
const Element = props => {
|
|
43
|
+
const {
|
|
44
|
+
attributes,
|
|
45
|
+
children,
|
|
46
|
+
element
|
|
47
|
+
} = props;
|
|
48
|
+
switch (element.type) {
|
|
49
|
+
case 'dc':
|
|
50
|
+
return React.createElement(DynamicContentElementComponent, {
|
|
51
|
+
...props
|
|
52
|
+
});
|
|
53
|
+
default:
|
|
54
|
+
return React.createElement("p", {
|
|
55
|
+
...attributes
|
|
56
|
+
}, children);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
export function renderElement(props) {
|
|
60
|
+
return React.createElement(Element, {
|
|
61
|
+
...props
|
|
62
|
+
});
|
|
63
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Descendant } from 'slate';
|
|
2
|
+
import { type DynamicContent } from '../DynamicContentPicker';
|
|
3
|
+
import { type RichMessageContent, type DynamicContentElement, type CustomElement } from './types';
|
|
4
|
+
export declare function isElement(x: Descendant): x is CustomElement;
|
|
5
|
+
export declare function getDcByPath(value: RichMessageContent, path: number[]): DynamicContentElement | undefined;
|
|
6
|
+
export declare function findAllDc(value: RichMessageContent): DynamicContentElement[];
|
|
7
|
+
export declare function findPath(value: RichMessageContent, elToFind: Descendant): number[];
|
|
8
|
+
export declare function parseDc(value: string): DynamicContent;
|
|
9
|
+
export declare function string2content(value: string): RichMessageContent;
|
|
10
|
+
export declare function stringifyDc(dc: DynamicContent): string;
|
|
11
|
+
export declare function content2string(state: RichMessageContent): string;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export function isElement(x) {
|
|
2
|
+
return !!x.type;
|
|
3
|
+
}
|
|
4
|
+
export function getDcByPath(value, path) {
|
|
5
|
+
let current = value;
|
|
6
|
+
for (let i = 0; i < path.length; i += 1) {
|
|
7
|
+
const el = current[path[i]];
|
|
8
|
+
if (el) {
|
|
9
|
+
if (isElement(el) && el.type === 'dc') {
|
|
10
|
+
return el;
|
|
11
|
+
}
|
|
12
|
+
} else {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
current = el.children;
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
export function findAllDc(value) {
|
|
20
|
+
const dces = [];
|
|
21
|
+
function walkDeep(children) {
|
|
22
|
+
for (const el of children) {
|
|
23
|
+
if (isElement(el)) {
|
|
24
|
+
if (el.type === 'dc') {
|
|
25
|
+
dces.push(el);
|
|
26
|
+
}
|
|
27
|
+
walkDeep(el.children);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
walkDeep(value);
|
|
32
|
+
return dces;
|
|
33
|
+
}
|
|
34
|
+
export function findPath(value, elToFind) {
|
|
35
|
+
for (let i = 0; i < value.length; i++) {
|
|
36
|
+
const el = value[i];
|
|
37
|
+
if (el === elToFind) {
|
|
38
|
+
return [i];
|
|
39
|
+
}
|
|
40
|
+
if (isElement(el)) {
|
|
41
|
+
const path = findPath(el.children, elToFind);
|
|
42
|
+
if (path.length > 0) {
|
|
43
|
+
return [i, ...path];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
export function parseDc(value) {
|
|
50
|
+
const [tagName, modifier, defaultValue = ''] = value.slice(1, -1).split('|');
|
|
51
|
+
return {
|
|
52
|
+
tagName,
|
|
53
|
+
modifier,
|
|
54
|
+
defaultValue
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export function string2content(value) {
|
|
58
|
+
return value.split('\n').map(line => ({
|
|
59
|
+
type: 'paragraph',
|
|
60
|
+
children: line.split(/({\w+\|[^}]+})/).map((text, index) => {
|
|
61
|
+
if (index % 2 === 1) {
|
|
62
|
+
return {
|
|
63
|
+
type: 'dc',
|
|
64
|
+
dc: parseDc(text),
|
|
65
|
+
children: [{
|
|
66
|
+
text: ''
|
|
67
|
+
}]
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
text
|
|
72
|
+
};
|
|
73
|
+
})
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
export function stringifyDc(dc) {
|
|
77
|
+
const {
|
|
78
|
+
tagName,
|
|
79
|
+
modifier,
|
|
80
|
+
defaultValue
|
|
81
|
+
} = dc;
|
|
82
|
+
return `{${tagName}|${modifier}${defaultValue ? `|${defaultValue}` : ''}}`;
|
|
83
|
+
}
|
|
84
|
+
export function content2string(state) {
|
|
85
|
+
return state.map(el => {
|
|
86
|
+
const {
|
|
87
|
+
children
|
|
88
|
+
} = el;
|
|
89
|
+
return children.map(el => {
|
|
90
|
+
if (isElement(el) && el.type === 'dc') {
|
|
91
|
+
return stringifyDc(el.dc);
|
|
92
|
+
}
|
|
93
|
+
return el.text;
|
|
94
|
+
}).join('');
|
|
95
|
+
}).join('\n');
|
|
96
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export const EmojiIcon = () => React.createElement("svg", {
|
|
3
|
+
width: "16",
|
|
4
|
+
height: "16",
|
|
5
|
+
viewBox: "0 0 16 16",
|
|
6
|
+
fill: "none",
|
|
7
|
+
xmlns: "http://www.w3.org/2000/svg"
|
|
8
|
+
}, React.createElement("path", {
|
|
9
|
+
fillRule: "evenodd",
|
|
10
|
+
clipRule: "evenodd",
|
|
11
|
+
d: "M2.75 8C2.75 5.10051 5.10051 2.75 8 2.75C10.8995 2.75 13.25 5.10051 13.25 8C13.25 10.8995 10.8995 13.25 8 13.25C5.10051 13.25 2.75 10.8995 2.75 8ZM8 1.25C4.27208 1.25 1.25 4.27208 1.25 8C1.25 11.7279 4.27208 14.75 8 14.75C11.7279 14.75 14.75 11.7279 14.75 8C14.75 4.27208 11.7279 1.25 8 1.25ZM5.66669 7.33334C6.21897 7.33334 6.66669 6.88563 6.66669 6.33334C6.66669 5.78106 6.21897 5.33334 5.66669 5.33334C5.1144 5.33334 4.66669 5.78106 4.66669 6.33334C4.66669 6.88563 5.1144 7.33334 5.66669 7.33334ZM11.3333 6.33334C11.3333 6.88563 10.8856 7.33334 10.3333 7.33334C9.78103 7.33334 9.33331 6.88563 9.33331 6.33334C9.33331 5.78106 9.78103 5.33334 10.3333 5.33334C10.8856 5.33334 11.3333 5.78106 11.3333 6.33334ZM11.4225 9.94557C11.6215 9.58228 11.4883 9.12647 11.125 8.9275C10.7617 8.72852 10.3059 8.86173 10.1069 9.22502C9.81509 9.75789 9.03837 10.2439 8.00002 10.2439C6.96166 10.2439 6.18494 9.75789 5.89309 9.22502C5.69412 8.86173 5.23831 8.72852 4.87501 8.9275C4.51172 9.12647 4.37852 9.58228 4.57749 9.94557C5.19205 11.0676 6.54974 11.7439 8.00002 11.7439C9.45029 11.7439 10.808 11.0676 11.4225 9.94557Z",
|
|
12
|
+
fill: "currentColor"
|
|
13
|
+
}));
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export const PersonalizationIcon = () => React.createElement("svg", {
|
|
3
|
+
width: "16",
|
|
4
|
+
height: "16",
|
|
5
|
+
viewBox: "0 0 16 16",
|
|
6
|
+
fill: "none",
|
|
7
|
+
xmlns: "http://www.w3.org/2000/svg"
|
|
8
|
+
}, React.createElement("path", {
|
|
9
|
+
fillRule: "evenodd",
|
|
10
|
+
clipRule: "evenodd",
|
|
11
|
+
d: "M5 1.25C5.41421 1.25 5.75 1.58579 5.75 2C5.75 2.41421 5.41421 2.75 5 2.75C3.75736 2.75 2.75 3.75736 2.75 5V6.75C2.75 7.13672 2.64024 7.49781 2.45017 7.80381C2.37602 7.92319 2.37602 8.07682 2.45017 8.1962C2.64024 8.50219 2.75 8.86328 2.75 9.25V11C2.75 12.2426 3.75736 13.25 5 13.25C5.41421 13.25 5.75 13.5858 5.75 14C5.75 14.4142 5.41421 14.75 5 14.75C2.92893 14.75 1.25 13.0711 1.25 11V9.25C1.25 8.97386 1.02614 8.75 0.75 8.75C0.335786 8.75 0 8.41421 0 8C0 7.58579 0.335786 7.25 0.75 7.25C1.02614 7.25 1.25 7.02614 1.25 6.75V5C1.25 2.92893 2.92893 1.25 5 1.25ZM11 1.25C10.5858 1.25 10.25 1.58579 10.25 2C10.25 2.41421 10.5858 2.75 11 2.75C12.2426 2.75 13.25 3.75736 13.25 5V6.75C13.25 7.13672 13.3598 7.49781 13.5498 7.80381C13.624 7.92319 13.624 8.07682 13.5498 8.1962C13.3598 8.50219 13.25 8.86328 13.25 9.25V11C13.25 12.2426 12.2426 13.25 11 13.25C10.5858 13.25 10.25 13.5858 10.25 14C10.25 14.4142 10.5858 14.75 11 14.75C13.0711 14.75 14.75 13.0711 14.75 11V9.25C14.75 8.97386 14.9739 8.75 15.25 8.75C15.6642 8.75 16 8.41421 16 8C16 7.58579 15.6642 7.25 15.25 7.25C14.9739 7.25 14.75 7.02614 14.75 6.75V5C14.75 2.92893 13.0711 1.25 11 1.25ZM4.00438 7.98268C4.00677 7.4304 4.45643 6.98464 5.00871 6.98703C5.56099 6.98943 6.00675 7.43909 6.00436 7.99137C6.00196 8.54365 5.5523 8.98941 5.00002 8.98702C4.44774 8.98462 4.00198 8.53496 4.00438 7.98268ZM8.00434 7.00001C7.45206 6.99761 7.00241 7.44338 7.00001 7.99566C6.99761 8.54794 7.44338 8.99759 7.99566 8.99999C8.54794 9.00239 8.99759 8.55662 8.99999 8.00434C9.00239 7.45206 8.55662 7.00241 8.00434 7.00001ZM11.0087 7.01304C10.4564 7.01064 10.0068 7.4564 10.0044 8.00868C10.002 8.56096 10.4477 9.01062 11 9.01302C11.5523 9.01541 12.002 8.56965 12.0044 8.01737C12.0068 7.46509 11.561 7.01543 11.0087 7.01304Z",
|
|
12
|
+
fill: "currentColor"
|
|
13
|
+
}));
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Descendant } from 'slate';
|
|
2
|
+
export type DynamicContent = {
|
|
3
|
+
tagName: string;
|
|
4
|
+
modifier: string;
|
|
5
|
+
defaultValue: string;
|
|
6
|
+
};
|
|
7
|
+
export type ParagraphElement = {
|
|
8
|
+
type: 'paragraph';
|
|
9
|
+
children: Descendant[];
|
|
10
|
+
};
|
|
11
|
+
export type CustomElement = DynamicContentElement | ParagraphElement;
|
|
12
|
+
export type CustomText = {
|
|
13
|
+
bold?: boolean;
|
|
14
|
+
italic?: boolean;
|
|
15
|
+
code?: boolean;
|
|
16
|
+
text: string;
|
|
17
|
+
};
|
|
18
|
+
export type RichMessageContent = Descendant[];
|
|
19
|
+
export type EmptyText = {
|
|
20
|
+
text: string;
|
|
21
|
+
};
|
|
22
|
+
export type DynamicContentElement = {
|
|
23
|
+
type: 'dc';
|
|
24
|
+
dc: DynamicContent;
|
|
25
|
+
children: EmptyText[];
|
|
26
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const withDynamicContent = editor => {
|
|
2
|
+
const {
|
|
3
|
+
isInline,
|
|
4
|
+
isVoid
|
|
5
|
+
} = editor;
|
|
6
|
+
editor.isInline = element => {
|
|
7
|
+
return element.type === 'dc' ? true : isInline(element);
|
|
8
|
+
};
|
|
9
|
+
editor.isVoid = element => {
|
|
10
|
+
return element.type === 'dc' ? true : isVoid(element);
|
|
11
|
+
};
|
|
12
|
+
return editor;
|
|
13
|
+
};
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -2,4 +2,5 @@ export * from './EmailTemplatePreview';
|
|
|
2
2
|
export * from './context';
|
|
3
3
|
export * from './DynamicContentPicker';
|
|
4
4
|
export * from './PlatformsConfigurationDrawer';
|
|
5
|
-
export * from './LanguagesSelector';
|
|
5
|
+
export * from './LanguagesSelector';
|
|
6
|
+
export * from './RichMessageEditor';
|
package/package.json
CHANGED
|
@@ -15,11 +15,15 @@
|
|
|
15
15
|
},
|
|
16
16
|
"pre-commit": "check",
|
|
17
17
|
"dependencies": {
|
|
18
|
+
"@emoji-mart/react": "^1.1.1",
|
|
18
19
|
"@pushwoosh/dumb-components": "^1.0.39",
|
|
19
20
|
"@pushwoosh/kit-constants": "^1.6.9",
|
|
20
21
|
"@pushwoosh/kit-helpers": "^4.8.1",
|
|
21
22
|
"@pushwoosh/kit-icons": "^2.1.1",
|
|
22
|
-
"@pushwoosh/kit-typography": "^1.7.3"
|
|
23
|
+
"@pushwoosh/kit-typography": "^1.7.3",
|
|
24
|
+
"slate": "^0.112.0",
|
|
25
|
+
"slate-history": "^0.110.3",
|
|
26
|
+
"slate-react": "^0.112.1"
|
|
23
27
|
},
|
|
24
28
|
"devDependencies": {
|
|
25
29
|
"@pushwoosh/frontend-builder-engine": "^1.0.17",
|
|
@@ -47,5 +51,5 @@
|
|
|
47
51
|
"engines": {
|
|
48
52
|
"node": ">= 18"
|
|
49
53
|
},
|
|
50
|
-
"version": "1.0.
|
|
54
|
+
"version": "1.0.7"
|
|
51
55
|
}
|