@kgalexander/mcreate 0.0.7 → 0.0.8

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
@@ -8,6 +8,20 @@ mcreate package
8
8
  npm install @kgalexander/mcreate
9
9
  ```
10
10
 
11
+ ## Tailwind CSS v4 Setup
12
+
13
+ This package uses Tailwind CSS utility classes. For styles to work correctly, you need to configure Tailwind to scan the package for class names.
14
+
15
+ Add the `@source` directive to your `globals.css` (or main CSS file):
16
+
17
+ ```css
18
+ @import "tailwindcss";
19
+
20
+ @source "../node_modules/@kgalexander/mcreate";
21
+ ```
22
+
23
+ > **Why is this needed?** Tailwind CSS v4 doesn't scan `node_modules` by default. The `@source` directive tells Tailwind to include class names from this package when compiling your CSS.
24
+
11
25
  ## Local Development with npm link
12
26
 
13
27
  When using `npm link` to develop this package locally with a Next.js application, you need to configure Next.js to resolve symlinked packages outside the project directory.
@@ -1875,6 +1875,8 @@ var useEditorStore = create()(
1875
1875
  immer((set) => ({
1876
1876
  // Initial state
1877
1877
  template: defaultTemplate,
1878
+ templateId: null,
1879
+ onSave: null,
1878
1880
  previewMode: false,
1879
1881
  focusIdx: null,
1880
1882
  hoverIdx: null,
@@ -1893,6 +1895,48 @@ var useEditorStore = create()(
1893
1895
  historyIndex: -1,
1894
1896
  isUndoRedoAction: false,
1895
1897
  lastHistoryPushTime: 0,
1898
+ // Auto-save tracking
1899
+ lastSavedSnapshot: null,
1900
+ isSaving: false,
1901
+ // Initialize store with external template (for npm package usage)
1902
+ initializeWithTemplate: (templateId, template, onSave) => {
1903
+ set((state) => {
1904
+ state.templateId = templateId;
1905
+ state.template = template;
1906
+ state.onSave = onSave ?? null;
1907
+ state.templateSize = calculateTemplateSize(template);
1908
+ state.isAtSizeLimit = false;
1909
+ state.history = [cloneDeep(template)];
1910
+ state.historyIndex = 0;
1911
+ state.focusIdx = null;
1912
+ state.hoverIdx = null;
1913
+ state.textEditing = null;
1914
+ state.lastSavedSnapshot = JSON.stringify(template);
1915
+ });
1916
+ },
1917
+ // Template metadata actions
1918
+ setTemplateName: (name) => {
1919
+ set((state) => {
1920
+ state.template.name = name;
1921
+ });
1922
+ useEditorStore.getState().pushHistory();
1923
+ },
1924
+ // Auto-save actions
1925
+ markAsSaved: () => {
1926
+ set((state) => {
1927
+ state.lastSavedSnapshot = JSON.stringify(state.template);
1928
+ });
1929
+ },
1930
+ hasUnsavedChanges: () => {
1931
+ const state = useEditorStore.getState();
1932
+ if (!state.lastSavedSnapshot) return true;
1933
+ return JSON.stringify(state.template) !== state.lastSavedSnapshot;
1934
+ },
1935
+ setIsSaving: (saving) => {
1936
+ set((state) => {
1937
+ state.isSaving = saving;
1938
+ });
1939
+ },
1896
1940
  // Selection actions
1897
1941
  setFocusIdx: (idx) => {
1898
1942
  set((state) => {
@@ -4341,6 +4385,11 @@ function DialogPortal({
4341
4385
  }) {
4342
4386
  return /* @__PURE__ */ jsx4(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
4343
4387
  }
4388
+ function DialogClose({
4389
+ ...props
4390
+ }) {
4391
+ return /* @__PURE__ */ jsx4(DialogPrimitive.Close, { "data-slot": "dialog-close", ...props });
4392
+ }
4344
4393
  function DialogOverlay({
4345
4394
  className,
4346
4395
  ...props
@@ -4392,6 +4441,38 @@ function DialogContent({
4392
4441
  )
4393
4442
  ] });
4394
4443
  }
4444
+ function DialogHeader({ className, ...props }) {
4445
+ return /* @__PURE__ */ jsx4(
4446
+ "div",
4447
+ {
4448
+ "data-slot": "dialog-header",
4449
+ className: cn("flex flex-col gap-2 text-center sm:text-left", className),
4450
+ ...props
4451
+ }
4452
+ );
4453
+ }
4454
+ function DialogFooter({
4455
+ className,
4456
+ showCloseButton = false,
4457
+ children,
4458
+ ...props
4459
+ }) {
4460
+ return /* @__PURE__ */ jsxs2(
4461
+ "div",
4462
+ {
4463
+ "data-slot": "dialog-footer",
4464
+ className: cn(
4465
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
4466
+ className
4467
+ ),
4468
+ ...props,
4469
+ children: [
4470
+ children,
4471
+ showCloseButton && /* @__PURE__ */ jsx4(DialogPrimitive.Close, { asChild: true, children: /* @__PURE__ */ jsx4(Button, { variant: "outline", children: "Close" }) })
4472
+ ]
4473
+ }
4474
+ );
4475
+ }
4395
4476
  function DialogTitle({
4396
4477
  className,
4397
4478
  ...props
@@ -14062,9 +14143,6 @@ function getCompanyFooterSection(template) {
14062
14143
  }
14063
14144
  function Editor({ setEditorLoading }) {
14064
14145
  useUndoRedoKeyboard();
14065
- useEffect19(() => {
14066
- useEditorStore.getState().clearHistory();
14067
- }, []);
14068
14146
  const template = useEditorStore((state) => state.template);
14069
14147
  const previewMode = useEditorStore((state) => state.previewMode);
14070
14148
  const focusIdx = useEditorStore((state) => state.focusIdx);
@@ -15003,6 +15081,7 @@ export {
15003
15081
  parseBorder,
15004
15082
  formatBorder,
15005
15083
  parsePrice,
15084
+ json2mjml,
15006
15085
  MAX_TEMPLATE_SIZE,
15007
15086
  useEditorStore,
15008
15087
  BUTTON_ALIGNMENTS,
@@ -15029,7 +15108,10 @@ export {
15029
15108
  Button,
15030
15109
  Dialog,
15031
15110
  DialogTrigger,
15111
+ DialogClose,
15032
15112
  DialogContent,
15113
+ DialogHeader,
15114
+ DialogFooter,
15033
15115
  DialogTitle,
15034
15116
  QUICK_BLOCKS,
15035
15117
  Tooltip,
@@ -5,7 +5,7 @@ import {
5
5
  MAILLOW_EMAIL_EDITOR_VERSION,
6
6
  Preview,
7
7
  useEditorStore
8
- } from "./chunk-4J3VEZPB.mjs";
8
+ } from "./chunk-JD4V766D.mjs";
9
9
  export {
10
10
  Editor,
11
11
  History,
package/dist/index.d.mts CHANGED
@@ -1,15 +1,294 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
 
3
+ type PageElement = {
4
+ id?: string;
5
+ type: 'page';
6
+ tagName: 'mj-body';
7
+ data: {
8
+ value: {
9
+ breakpoint?: string;
10
+ headAttributes?: string;
11
+ fontSize?: string;
12
+ fontWeight?: string;
13
+ lineHeight?: string;
14
+ 'font-size'?: string;
15
+ 'font-weight'?: string;
16
+ 'line-height'?: string;
17
+ headStyles?: string[];
18
+ fonts?: {
19
+ name?: string;
20
+ href?: string;
21
+ }[];
22
+ responsive?: boolean;
23
+ fontFamily?: string;
24
+ 'font-family'?: string;
25
+ textColor?: string;
26
+ 'text-color'?: string;
27
+ showCompanyFooter?: boolean;
28
+ [key: string]: any;
29
+ };
30
+ };
31
+ attributes: {
32
+ [key: string]: string;
33
+ };
34
+ children: (SectionElement | SectionColumnElement | SectionPropertyKmElement | SectionPropertySingleTwoElement | SectionPropertyTripleElement)[];
35
+ };
36
+ type SectionElement = {
37
+ type: 'section';
38
+ tagName: 'mj-section';
39
+ data: {
40
+ value: {
41
+ columnRows?: number;
42
+ columnWidths?: string[];
43
+ noWrap?: boolean;
44
+ isCompanyFooter?: boolean;
45
+ [key: string]: any;
46
+ };
47
+ };
48
+ attributes: {
49
+ [key: string]: string;
50
+ };
51
+ children: (ColumnElement)[];
52
+ };
53
+ type SectionColumnElement = {
54
+ type: 'section-column';
55
+ tagName: 'mj-section';
56
+ data: {
57
+ value: {
58
+ columnRows?: number;
59
+ columnWidths?: string[];
60
+ noWrap?: boolean;
61
+ [key: string]: any;
62
+ };
63
+ };
64
+ attributes: {
65
+ [key: string]: string;
66
+ };
67
+ children: (ColumnElement)[];
68
+ };
69
+ type SectionPropertyKmElement = {
70
+ type: 'section-property-km';
71
+ tagName: 'mj-section';
72
+ data: {
73
+ value: {
74
+ noWrap?: boolean;
75
+ [key: string]: any;
76
+ };
77
+ };
78
+ attributes: {
79
+ [key: string]: string;
80
+ };
81
+ children: [ColumnElement];
82
+ };
83
+ type SectionPropertySingleTwoElement = {
84
+ type: 'section-property-single-two';
85
+ tagName: 'mj-section';
86
+ data: {
87
+ value: {
88
+ noWrap?: boolean;
89
+ [key: string]: any;
90
+ };
91
+ };
92
+ attributes: {
93
+ [key: string]: string;
94
+ };
95
+ children: [ColumnElement];
96
+ };
97
+ type ColumnElement = {
98
+ type: 'column';
99
+ tagName: 'mj-column';
100
+ data: {
101
+ value: {
102
+ [key: string]: any;
103
+ };
104
+ };
105
+ attributes: {
106
+ [key: string]: string;
107
+ };
108
+ children: (TextElement | ButtonElement | SpaceElement | DividerElement | ImageElement | SocialElement | PropertyCardElement | PropertyCardSingleTwoElement | PropertyCardTripleElement)[];
109
+ };
110
+ type TextElement = {
111
+ type: 'text';
112
+ tagName: 'mj-text';
113
+ data: {
114
+ value: {
115
+ content: string;
116
+ [key: string]: any;
117
+ };
118
+ };
119
+ attributes: {
120
+ [key: string]: string;
121
+ };
122
+ };
123
+ type ButtonElement = {
124
+ type: 'button';
125
+ tagName: 'mj-button';
126
+ data: {
127
+ value: {
128
+ content: string;
129
+ [key: string]: any;
130
+ };
131
+ };
132
+ attributes: {
133
+ [key: string]: string;
134
+ };
135
+ };
136
+ type PropertyCardElement = {
137
+ type: 'property-card';
138
+ tagName: 'mj-colproperty';
139
+ data: {
140
+ value: {
141
+ [key: string]: any;
142
+ };
143
+ };
144
+ attributes: {
145
+ [key: string]: string;
146
+ };
147
+ };
148
+ type PropertyCardSingleTwoElement = {
149
+ type: 'property-card-single-two';
150
+ tagName: 'mj-propertysingletwo';
151
+ data: {
152
+ value: {
153
+ [key: string]: any;
154
+ };
155
+ };
156
+ attributes: {
157
+ [key: string]: string;
158
+ };
159
+ };
160
+ type PropertyCardTripleItemElement = {
161
+ type: 'property-card-triple-item';
162
+ tagName: 'mj-propertytripleitem';
163
+ data: {
164
+ value: {
165
+ [key: string]: any;
166
+ };
167
+ };
168
+ attributes: {
169
+ [key: string]: string;
170
+ };
171
+ };
172
+ type PropertyCardTripleElement = {
173
+ type: 'property-card-triple';
174
+ tagName: 'mj-propertytriple';
175
+ data: {
176
+ value: {
177
+ [key: string]: any;
178
+ };
179
+ };
180
+ attributes: {
181
+ [key: string]: string;
182
+ };
183
+ children: PropertyCardTripleItemElement[];
184
+ };
185
+ type SectionPropertyTripleElement = {
186
+ type: 'section-property-triple';
187
+ tagName: 'mj-section';
188
+ data: {
189
+ value: {
190
+ noWrap?: boolean;
191
+ [key: string]: any;
192
+ };
193
+ };
194
+ attributes: {
195
+ [key: string]: string;
196
+ };
197
+ children: [ColumnElement];
198
+ };
199
+ type SpaceElement = {
200
+ type: 'space';
201
+ tagName: 'mj-spacer';
202
+ attributes: {
203
+ [key: string]: string;
204
+ };
205
+ };
206
+ type DividerElement = {
207
+ type: 'divider';
208
+ tagName: 'mj-divider';
209
+ attributes: {
210
+ [key: string]: string;
211
+ };
212
+ };
213
+ type ImageElement = {
214
+ type: 'image';
215
+ tagName: 'mj-image';
216
+ attributes: {
217
+ [key: string]: string;
218
+ };
219
+ };
220
+ type SocialStyle = 'Icon + Text' | 'Icon' | 'Text';
221
+ type IconSize = 'Small' | 'Medium' | 'Large';
222
+ type IconStyle = 'Regular' | 'Filled' | 'Outline';
223
+ type IconColor = 'Colored' | 'Dark' | 'Light' | 'Gray';
224
+ type SocialElement = {
225
+ type: 'social';
226
+ tagName: 'mj-social';
227
+ data: {
228
+ value: {
229
+ socialStyle: SocialStyle;
230
+ iconSize: IconSize;
231
+ iconStyle: IconStyle;
232
+ iconColor: IconColor;
233
+ };
234
+ };
235
+ attributes: {
236
+ [key: string]: string;
237
+ };
238
+ children: (SocialItemElement)[];
239
+ };
240
+ type SocialItemElement = {
241
+ type: 'social-item';
242
+ tagName: 'mj-social-element';
243
+ data: {
244
+ value: {
245
+ content: string;
246
+ socialType: string;
247
+ };
248
+ };
249
+ attributes: {
250
+ [key: string]: string;
251
+ };
252
+ };
253
+ type TemplateJSON = {
254
+ id: string;
255
+ name: string;
256
+ version: string;
257
+ published: boolean;
258
+ creator: string;
259
+ content: PageElement[];
260
+ };
261
+
262
+ type OnSaveCallback = (templateId: string, template: TemplateJSON) => void | Promise<void>;
263
+
3
264
  interface EditorProps {
4
- defaultContent?: string;
5
265
  setEditorLoading?: (loading: boolean) => void;
6
266
  editorLoading?: boolean;
7
267
  }
8
268
  declare function Editor({ setEditorLoading }: EditorProps): react_jsx_runtime.JSX.Element;
9
269
 
10
- declare function TemplatePage({ templateId, initialTemplate }: {
270
+ declare function TemplatePage({ templateId, initialTemplate, onSave }: {
11
271
  templateId: string;
12
- initialTemplate: any;
272
+ initialTemplate: TemplateJSON;
273
+ onSave?: OnSaveCallback;
13
274
  }): react_jsx_runtime.JSX.Element;
14
275
 
15
- export { Editor, TemplatePage };
276
+ /**
277
+ * JSON to MJML Converter
278
+ * Converts template JSON to MJML string for rendering
279
+ */
280
+
281
+ type RenderMode = 'production' | 'editing';
282
+ interface RenderOptions {
283
+ isPaid?: boolean;
284
+ }
285
+ /**
286
+ * Convert template JSON to MJML string
287
+ * @param template - Template JSON object
288
+ * @param mode - Render mode: 'production' for clean output, 'editing' for tracking classes
289
+ * @param options - Render options including isPaid for company footer visibility
290
+ * @returns MJML string
291
+ */
292
+ declare function json2mjml(template: TemplateJSON, mode?: RenderMode, options?: RenderOptions): string;
293
+
294
+ export { Editor, type OnSaveCallback, type TemplateJSON, TemplatePage, json2mjml };