react-tailwind-email-editor 0.0.16 → 0.0.18

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/README.md CHANGED
@@ -154,6 +154,7 @@ function App() {
154
154
  | `templates` | `EmailTemplate[]` | `[]` | Pre-defined templates |
155
155
  | `callbacks` | `EditorCallbacks` | `{}` | Event handlers (onExport, onChange, etc.) |
156
156
  | `initialState` | `string` | — | Serialized Craft.js state to restore |
157
+ | `initialHtml` | `string` | — | HTML string to parse and load into the editor |
157
158
  | `defaultContent` | `ReactNode` | — | Default JSX content for the canvas |
158
159
  | `title` | `string` | `'Email Editor'` | Toolbar title |
159
160
  | `logo` | `ReactNode` | — | Custom logo element |
package/dist/index.d.cts CHANGED
@@ -97,6 +97,8 @@ interface EmailEditorProps {
97
97
  templates?: EmailTemplate[];
98
98
  callbacks?: EditorCallbacks;
99
99
  initialState?: string;
100
+ /** HTML string to parse and load into the editor on mount */
101
+ initialHtml?: string;
100
102
  defaultContent?: React.ReactNode;
101
103
  title?: string;
102
104
  logo?: React.ReactNode;
package/dist/index.d.ts CHANGED
@@ -97,6 +97,8 @@ interface EmailEditorProps {
97
97
  templates?: EmailTemplate[];
98
98
  callbacks?: EditorCallbacks;
99
99
  initialState?: string;
100
+ /** HTML string to parse and load into the editor on mount */
101
+ initialHtml?: string;
100
102
  defaultContent?: React.ReactNode;
101
103
  title?: string;
102
104
  logo?: React.ReactNode;
package/dist/index.js CHANGED
@@ -4024,6 +4024,160 @@ var TabsContent = React9__namespace.forwardRef(({ className, ...props }, ref) =>
4024
4024
  }
4025
4025
  ));
4026
4026
  TabsContent.displayName = TabsPrimitive__namespace.Content.displayName;
4027
+
4028
+ // src/lib/htmlImporter.ts
4029
+ var nodeCounter = 0;
4030
+ var generateNodeId = () => {
4031
+ nodeCounter++;
4032
+ return `imported_${nodeCounter}_${Math.random().toString(36).slice(2, 8)}`;
4033
+ };
4034
+ var decodeProps = (encoded) => {
4035
+ try {
4036
+ const json = atob(encoded);
4037
+ const parsed = JSON.parse(json);
4038
+ const { _type, ...props } = parsed;
4039
+ return props;
4040
+ } catch (e) {
4041
+ return null;
4042
+ }
4043
+ };
4044
+ var importHtmlToState = (html) => {
4045
+ nodeCounter = 0;
4046
+ const parser = new DOMParser();
4047
+ const doc = parser.parseFromString(html, "text/html");
4048
+ const componentEls = doc.querySelectorAll("[data-component]");
4049
+ const nodes = {};
4050
+ const childNodeIds = [];
4051
+ const topLevelComponents = [];
4052
+ componentEls.forEach((el) => {
4053
+ let parent = el.parentElement;
4054
+ let isNested = false;
4055
+ while (parent) {
4056
+ if (parent.hasAttribute("data-component")) {
4057
+ isNested = true;
4058
+ break;
4059
+ }
4060
+ parent = parent.parentElement;
4061
+ }
4062
+ if (!isNested) {
4063
+ topLevelComponents.push(el);
4064
+ }
4065
+ });
4066
+ if (topLevelComponents.length === 0) {
4067
+ const rawNodeId = generateNodeId();
4068
+ nodes[rawNodeId] = {
4069
+ type: { resolvedName: "RawHtml" },
4070
+ isCanvas: false,
4071
+ props: { html, padding: 0 },
4072
+ displayName: "RawHtml",
4073
+ custom: {},
4074
+ hidden: false,
4075
+ nodes: [],
4076
+ linkedNodes: {},
4077
+ parent: "ROOT"
4078
+ };
4079
+ childNodeIds.push(rawNodeId);
4080
+ } else {
4081
+ for (const el of topLevelComponents) {
4082
+ const typeName = el.getAttribute("data-component") || "RawHtml";
4083
+ const encodedProps = el.getAttribute("data-props") || "";
4084
+ const props = decodeProps(encodedProps);
4085
+ if (!props) continue;
4086
+ const nodeId = generateNodeId();
4087
+ const isCanvas = typeName === "Container" || typeName === "Paper";
4088
+ const nestedChildren = [];
4089
+ const nestedLinkedNodes = {};
4090
+ if (typeName === "Container") {
4091
+ const nestedEls = el.querySelectorAll(":scope > div > [data-component], :scope [data-component]");
4092
+ nestedEls.forEach((nestedEl) => {
4093
+ let nestedParent = nestedEl.parentElement;
4094
+ let belongsToThis = false;
4095
+ while (nestedParent && nestedParent !== el) {
4096
+ if (nestedParent.hasAttribute("data-component") && nestedParent !== el) {
4097
+ break;
4098
+ }
4099
+ if (nestedParent === el) {
4100
+ belongsToThis = true;
4101
+ }
4102
+ nestedParent = nestedParent.parentElement;
4103
+ }
4104
+ if (nestedParent === el) belongsToThis = true;
4105
+ if (belongsToThis) {
4106
+ const nestedType = nestedEl.getAttribute("data-component") || "RawHtml";
4107
+ const nestedEncoded = nestedEl.getAttribute("data-props") || "";
4108
+ const nestedProps = decodeProps(nestedEncoded);
4109
+ if (nestedProps) {
4110
+ const childId = generateNodeId();
4111
+ nodes[childId] = {
4112
+ type: { resolvedName: nestedType },
4113
+ isCanvas: false,
4114
+ props: nestedProps,
4115
+ displayName: nestedType,
4116
+ custom: {},
4117
+ hidden: false,
4118
+ nodes: [],
4119
+ linkedNodes: {},
4120
+ parent: nodeId
4121
+ };
4122
+ nestedChildren.push(childId);
4123
+ }
4124
+ }
4125
+ });
4126
+ }
4127
+ if (typeName === "TwoColumn") {
4128
+ const leftId = generateNodeId();
4129
+ const rightId = generateNodeId();
4130
+ nodes[leftId] = {
4131
+ type: { resolvedName: "Container" },
4132
+ isCanvas: true,
4133
+ props: { background: "#f9f9f9", padding: 10 },
4134
+ displayName: "Container",
4135
+ custom: {},
4136
+ hidden: false,
4137
+ nodes: [],
4138
+ linkedNodes: {},
4139
+ parent: nodeId
4140
+ };
4141
+ nodes[rightId] = {
4142
+ type: { resolvedName: "Container" },
4143
+ isCanvas: true,
4144
+ props: { background: "#f9f9f9", padding: 10 },
4145
+ displayName: "Container",
4146
+ custom: {},
4147
+ hidden: false,
4148
+ nodes: [],
4149
+ linkedNodes: {},
4150
+ parent: nodeId
4151
+ };
4152
+ nestedLinkedNodes["left-column"] = leftId;
4153
+ nestedLinkedNodes["right-column"] = rightId;
4154
+ }
4155
+ nodes[nodeId] = {
4156
+ type: { resolvedName: typeName },
4157
+ isCanvas,
4158
+ props,
4159
+ displayName: typeName,
4160
+ custom: {},
4161
+ hidden: false,
4162
+ nodes: nestedChildren,
4163
+ linkedNodes: nestedLinkedNodes,
4164
+ parent: "ROOT"
4165
+ };
4166
+ childNodeIds.push(nodeId);
4167
+ }
4168
+ }
4169
+ nodes["ROOT"] = {
4170
+ type: { resolvedName: "Paper" },
4171
+ isCanvas: true,
4172
+ props: { background: "#ffffff" },
4173
+ displayName: "Paper",
4174
+ custom: {},
4175
+ hidden: false,
4176
+ nodes: childNodeIds,
4177
+ linkedNodes: {}
4178
+ };
4179
+ return JSON.stringify(nodes);
4180
+ };
4027
4181
  var Modal = ({
4028
4182
  open,
4029
4183
  onClose,
@@ -4725,7 +4879,8 @@ var EmailEditor = ({
4725
4879
  showToolbox = true,
4726
4880
  darkMode = true,
4727
4881
  className,
4728
- style
4882
+ style,
4883
+ initialHtml
4729
4884
  }) => {
4730
4885
  var _a, _b, _c, _d;
4731
4886
  const mergedComponents = React9.useMemo(() => {
@@ -4843,7 +4998,7 @@ var EmailEditor = ({
4843
4998
  onClick: () => {
4844
4999
  setActiveTab("properties");
4845
5000
  },
4846
- children: /* @__PURE__ */ jsxRuntime.jsx(core.Frame, { data: initialState, children: defaultEmailContent })
5001
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-8", children: initialState ? /* @__PURE__ */ jsxRuntime.jsx(core.Frame, { data: initialState, children: defaultEmailContent }) : initialHtml ? /* @__PURE__ */ jsxRuntime.jsx(core.Frame, { data: importHtmlToState(initialHtml), children: defaultEmailContent }) : /* @__PURE__ */ jsxRuntime.jsx(core.Frame, { children: defaultContent || defaultEmailContent }) })
4847
5002
  }
4848
5003
  )
4849
5004
  }