@servicetitan/dte-unlayer 0.0.1-test1

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.
Files changed (67) hide show
  1. package/dist/api-core.d.ts +19 -0
  2. package/dist/api-core.d.ts.map +1 -0
  3. package/dist/api-core.js +121 -0
  4. package/dist/api-core.js.map +1 -0
  5. package/dist/api-custom-tools.d.ts +18 -0
  6. package/dist/api-custom-tools.d.ts.map +1 -0
  7. package/dist/api-custom-tools.js +96 -0
  8. package/dist/api-custom-tools.js.map +1 -0
  9. package/dist/editor.d.ts +19 -0
  10. package/dist/editor.d.ts.map +1 -0
  11. package/dist/editor.js +47 -0
  12. package/dist/editor.js.map +1 -0
  13. package/dist/index.d.ts +12 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +11 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/loadScript.d.ts +2 -0
  18. package/dist/loadScript.d.ts.map +1 -0
  19. package/dist/loadScript.js +24 -0
  20. package/dist/loadScript.js.map +1 -0
  21. package/dist/shared/const.d.ts +27 -0
  22. package/dist/shared/const.d.ts.map +1 -0
  23. package/dist/shared/const.js +6 -0
  24. package/dist/shared/const.js.map +1 -0
  25. package/dist/shared/fonts.d.ts +10 -0
  26. package/dist/shared/fonts.d.ts.map +1 -0
  27. package/dist/shared/fonts.js +127 -0
  28. package/dist/shared/fonts.js.map +1 -0
  29. package/dist/shared/schema.d.ts +51 -0
  30. package/dist/shared/schema.d.ts.map +1 -0
  31. package/dist/shared/schema.js +104 -0
  32. package/dist/shared/schema.js.map +1 -0
  33. package/dist/shared/tools.d.ts +14 -0
  34. package/dist/shared/tools.d.ts.map +1 -0
  35. package/dist/shared/tools.js +8 -0
  36. package/dist/shared/tools.js.map +1 -0
  37. package/dist/store.d.ts +31 -0
  38. package/dist/store.d.ts.map +1 -0
  39. package/dist/store.js +274 -0
  40. package/dist/store.js.map +1 -0
  41. package/dist/tools.d.ts +26 -0
  42. package/dist/tools.d.ts.map +1 -0
  43. package/dist/tools.js +164 -0
  44. package/dist/tools.js.map +1 -0
  45. package/dist/unlayer-interface.d.ts +64 -0
  46. package/dist/unlayer-interface.d.ts.map +1 -0
  47. package/dist/unlayer-interface.js +2 -0
  48. package/dist/unlayer-interface.js.map +1 -0
  49. package/dist/unlayer.d.ts +57 -0
  50. package/dist/unlayer.d.ts.map +1 -0
  51. package/dist/unlayer.js +117 -0
  52. package/dist/unlayer.js.map +1 -0
  53. package/package.json +20 -0
  54. package/src/__tests__/schema.test.ts +96 -0
  55. package/src/api-core.ts +94 -0
  56. package/src/api-custom-tools.ts +79 -0
  57. package/src/editor.tsx +87 -0
  58. package/src/index.ts +11 -0
  59. package/src/loadScript.ts +27 -0
  60. package/src/shared/const.ts +30 -0
  61. package/src/shared/fonts.ts +126 -0
  62. package/src/shared/schema.ts +190 -0
  63. package/src/shared/tools.ts +18 -0
  64. package/src/store.ts +197 -0
  65. package/src/tools.ts +213 -0
  66. package/src/unlayer-interface.tsx +71 -0
  67. package/src/unlayer.tsx +176 -0
package/src/store.ts ADDED
@@ -0,0 +1,197 @@
1
+ import { loadScript } from './loadScript';
2
+ import { UnlayerEventConfig, UnlayerEventRegister } from './shared/const';
3
+ import {
4
+ createUnlayerEditor,
5
+ DesignUpdatedEventType,
6
+ EventDesignUpdated,
7
+ Unlayer,
8
+ } from './unlayer';
9
+ import { CreateUnlayerEditorProps, UnlayerDesignFormat, UnlayerRef } from './unlayer-interface';
10
+
11
+ const defaultScriptUrl = 'https://editor.unlayer.com/embed.js?2';
12
+
13
+ export interface UnlayerDesignChangeInfo {
14
+ isToolsListChanged: boolean;
15
+ event: any;
16
+ }
17
+
18
+ const eventsNotChangingToolsList: DesignUpdatedEventType[] = [
19
+ DesignUpdatedEventType.ContentMoved,
20
+ DesignUpdatedEventType.ContentModified,
21
+ DesignUpdatedEventType.ColumnModified,
22
+ DesignUpdatedEventType.RowMoved,
23
+ DesignUpdatedEventType.RowModified,
24
+ DesignUpdatedEventType.BodyModified,
25
+ ];
26
+
27
+ export class UnlayerStore {
28
+ readonly unlayerRef: UnlayerRef;
29
+
30
+ private editor: Unlayer | undefined;
31
+ private isInit = false;
32
+ private iframe?: HTMLIFrameElement;
33
+ private hasDesign = false;
34
+
35
+ private onMessageCB?: (type: string, data: any) => void;
36
+ private onChangeCB?: (info: UnlayerDesignChangeInfo) => void;
37
+ private onReadyCB?: () => void;
38
+ private onImageCB?: (file: File) => Promise<{ url: string }>;
39
+
40
+ constructor(readonly props: CreateUnlayerEditorProps) {
41
+ this.unlayerRef = {
42
+ loadDesign: design => {
43
+ this.editor?.loadDesign(design);
44
+ },
45
+ saveDesign: cb => {
46
+ this.editor?.saveDesign(cb);
47
+ },
48
+ exportHtml: cb => {
49
+ this.editor?.exportHtml(data => {
50
+ cb(data);
51
+ });
52
+ },
53
+ };
54
+ }
55
+
56
+ init = async (container: HTMLDivElement) => {
57
+ if (this.isInit) {
58
+ return;
59
+ }
60
+
61
+ this.isInit = true;
62
+
63
+ window.addEventListener('message', this.onPostMessage);
64
+
65
+ await loadScript(defaultScriptUrl);
66
+
67
+ this.editor = createUnlayerEditor(container, this.props);
68
+ this.editor.addEventListener('design:loaded', this.onDesignLoaded);
69
+ this.editor.addEventListener('design:updated', this.onDesignUpdated);
70
+ this.editor.registerCallback('image', this.uploadImage);
71
+
72
+ this.iframe = container.querySelector(`iframe`) ?? undefined;
73
+ };
74
+
75
+ destroy = () => {
76
+ window.removeEventListener('message', this.onPostMessage);
77
+
78
+ if (this.editor) {
79
+ this.editor.removeEventListener('design:loaded', this.onDesignLoaded);
80
+ this.editor.removeEventListener('design:updated', this.onDesignUpdated);
81
+ this.editor.unregisterCallback('image', this.uploadImage);
82
+
83
+ this.editor = undefined;
84
+ }
85
+ };
86
+
87
+ setDesign = (design?: UnlayerDesignFormat) => {
88
+ if (!this.editor) {
89
+ return;
90
+ }
91
+
92
+ this.hasDesign = !!design;
93
+
94
+ if (design) {
95
+ this.editor.loadDesign(design);
96
+ }
97
+ };
98
+
99
+ setOnChange = (onChange?: UnlayerStore['onChangeCB']) => {
100
+ this.onChangeCB = onChange;
101
+ };
102
+
103
+ setOnReady = (onReady?: UnlayerStore['onReadyCB']) => {
104
+ this.onReadyCB = onReady;
105
+ };
106
+
107
+ setOnImage = (onImage?: UnlayerStore['onImageCB']) => {
108
+ this.onImageCB = onImage;
109
+ };
110
+
111
+ setOnMessage = (onMessage?: UnlayerStore['onMessageCB']) => {
112
+ this.onMessageCB = onMessage;
113
+ };
114
+
115
+ private onDesignLoaded = () => {
116
+ if (!this.hasDesign) {
117
+ this.hasDesign = true;
118
+ this.editor?.setBodyValues({
119
+ backgroundColor: 'rgba(0,0,0,0)',
120
+ contentWidth: '100%',
121
+ });
122
+ }
123
+ };
124
+
125
+ private onDesignUpdated = (event: EventDesignUpdated) => {
126
+ this.onChangeCB?.({
127
+ isToolsListChanged: !eventsNotChangingToolsList.includes(event.type),
128
+ event,
129
+ });
130
+ };
131
+
132
+ private onPostMessage = (e: MessageEvent) => {
133
+ if (!e.data?.['-dte-message-from']) {
134
+ return;
135
+ }
136
+
137
+ const type = e.data?.['-dte-message-from']?.type;
138
+ const data = e.data?.['-dte-message-from']?.data;
139
+
140
+ if (!type) {
141
+ return;
142
+ }
143
+
144
+ if (type === '--ready' || type === '--registered') {
145
+ this.onReadyCB?.();
146
+ } else if (type === '--core-loaded') {
147
+ const data: UnlayerEventConfig = {
148
+ dummyData: this.props.dummyData,
149
+ schema: this.props.schema,
150
+ generics: this.props.generics,
151
+ genericConfigMode: this.props.genericConfigMode,
152
+ };
153
+
154
+ this.sendMessage('--config', JSON.parse(JSON.stringify(data)));
155
+ } else if (type === '--data-loaded') {
156
+ const data: UnlayerEventRegister = {
157
+ customTools: this.props.tools.map(tool => ({ key: tool.key })),
158
+ units: this.props.units?.map(unit => ({
159
+ ...unit,
160
+ values: {
161
+ ...unit.values,
162
+ adminConfig: undefined,
163
+ },
164
+ })),
165
+ };
166
+
167
+ this.sendMessage('--register', JSON.parse(JSON.stringify(data)));
168
+ }
169
+
170
+ this.onMessageCB?.(type, data);
171
+ };
172
+
173
+ private sendMessage = (type: string, data?: any) => {
174
+ this.iframe?.contentWindow?.postMessage(
175
+ {
176
+ '-dte-message-to': {
177
+ type,
178
+ data,
179
+ },
180
+ },
181
+ '*'
182
+ );
183
+ };
184
+
185
+ private uploadImage = (data: any, done: (result: any) => void) => {
186
+ if (!this.onImageCB) {
187
+ done({ progress: 100 });
188
+ throw new Error('image upload is not implemented');
189
+ }
190
+
191
+ if (data.attachments?.length) {
192
+ this.onImageCB(data.attachments[0])
193
+ .then(({ url }) => done({ progress: 100, url }))
194
+ .catch(() => done({ progress: 100 }));
195
+ }
196
+ };
197
+ }
package/src/tools.ts ADDED
@@ -0,0 +1,213 @@
1
+ import { constGenericsEditor } from './shared/const';
2
+ import { unlayerUnitsParseToolKey } from './shared/tools';
3
+ import { versions } from './unlayer';
4
+ import { UnlayerDesignFormat, UnlayerDesignTool } from './unlayer-interface';
5
+
6
+ /* eslint-disable @typescript-eslint/naming-convention */
7
+ export const unlayerGetDefaultDesign = (): UnlayerDesignFormat => ({
8
+ counters: {
9
+ u_column: 1,
10
+ u_row: 1,
11
+ u_content_custom_generic_list_of_fields: 1,
12
+ },
13
+ body: {
14
+ rows: [
15
+ {
16
+ cells: [1],
17
+ columns: [
18
+ {
19
+ contents: [],
20
+ values: {
21
+ backgroundColor: '',
22
+ padding: '0px',
23
+ border: {},
24
+ _meta: {
25
+ htmlID: 'u_column_1',
26
+ htmlClassNames: 'u_column',
27
+ },
28
+ },
29
+ },
30
+ ],
31
+ values: {
32
+ displayCondition: null,
33
+ columns: false,
34
+ backgroundColor: '',
35
+ columnsBackgroundColor: '',
36
+ backgroundImage: {
37
+ url: '',
38
+ fullWidth: true,
39
+ repeat: false,
40
+ center: true,
41
+ cover: false,
42
+ },
43
+ padding: '0px',
44
+ hideDesktop: false,
45
+ hideMobile: false,
46
+ noStackMobile: false,
47
+ _meta: {
48
+ htmlID: 'u_row_1',
49
+ htmlClassNames: 'u_row',
50
+ },
51
+ selectable: true,
52
+ draggable: true,
53
+ duplicatable: true,
54
+ deletable: true,
55
+ },
56
+ },
57
+ ],
58
+ values: {
59
+ backgroundColor: 'rgba(0,0,0,0)',
60
+ backgroundImage: {
61
+ url: '',
62
+ fullWidth: true,
63
+ repeat: false,
64
+ center: true,
65
+ cover: false,
66
+ },
67
+ contentWidth: '100%',
68
+ contentAlign: 'center',
69
+ fontFamily: {
70
+ label: 'Arial',
71
+ value: 'arial,helvetica,sans-serif',
72
+ },
73
+ preheaderText: '',
74
+ linkStyle: {
75
+ body: true,
76
+ linkColor: '#0000ee',
77
+ linkHoverColor: '#0000ee',
78
+ linkUnderline: true,
79
+ linkHoverUnderline: true,
80
+ },
81
+ _meta: {
82
+ htmlID: 'u_body',
83
+ htmlClassNames: 'u_body',
84
+ },
85
+ },
86
+ },
87
+ schemaVersion: versions.schema,
88
+ });
89
+ /* eslint-enable @typescript-eslint/naming-convention */
90
+
91
+ export const unlayerToolsIterate = (
92
+ design: UnlayerDesignFormat,
93
+ cb: (tool: UnlayerDesignTool) => boolean | undefined | void
94
+ ) => {
95
+ for (const row of design?.body?.rows ?? []) {
96
+ for (const column of row.columns) {
97
+ for (const content of column.contents) {
98
+ if (cb(content) === false) {
99
+ return;
100
+ }
101
+ }
102
+ }
103
+ }
104
+ };
105
+
106
+ export const unlayerToolsIterateCustom = (
107
+ design: UnlayerDesignFormat,
108
+ cb: (tool: UnlayerDesignTool) => boolean | undefined | void
109
+ ) => {
110
+ unlayerToolsIterate(design, tool => {
111
+ if (tool.type === 'custom') {
112
+ return cb(tool);
113
+ }
114
+ });
115
+ };
116
+
117
+ export const unlayerToolsListCustomKeys = (design: UnlayerDesignFormat): string[] => {
118
+ return Array.from(new Set(unlayerToolsListCustom(design).map(tool => tool.slug)));
119
+ };
120
+
121
+ export const unlayerToolsFind = (
122
+ design: UnlayerDesignFormat,
123
+ cb: (tool: UnlayerDesignTool) => boolean | void
124
+ ): UnlayerDesignTool | undefined => {
125
+ let out: UnlayerDesignTool | undefined;
126
+
127
+ unlayerToolsIterate(design, tool => {
128
+ if (cb(tool)) {
129
+ out = tool;
130
+ return false;
131
+ }
132
+ });
133
+
134
+ return out;
135
+ };
136
+
137
+ export interface UnlayerCustomToolsStat {
138
+ counters: Record<string, number>;
139
+ missing: string[];
140
+ notSupported: string[];
141
+ }
142
+
143
+ export const unlayerToolsCustomStat = (
144
+ design: UnlayerDesignFormat,
145
+ tools?: { key: string; isRequired?: boolean }[],
146
+ units?: { id: string; generic: string; isRequired?: boolean }[]
147
+ ): UnlayerCustomToolsStat => {
148
+ const counters: Record<string, number> = {};
149
+ const notSupported = new Set<string>();
150
+ const unitIDs = new Set<string>();
151
+
152
+ unlayerToolsIterateCustom(design, tool => {
153
+ if (!counters[tool.slug]) {
154
+ counters[tool.slug] = 0;
155
+ }
156
+
157
+ counters[tool.slug]++;
158
+
159
+ const isUnit = unlayerUnitsParseToolKey(tool.slug);
160
+
161
+ if (isUnit) {
162
+ unitIDs.add(isUnit.id);
163
+ }
164
+
165
+ if (
166
+ (isUnit && !units?.some(unit => unit.id === isUnit.id)) ||
167
+ (!isUnit && !tools?.some(t => t.key === tool.slug))
168
+ ) {
169
+ notSupported.add(tool.slug);
170
+ }
171
+ });
172
+
173
+ return {
174
+ counters,
175
+ notSupported: Array.from(notSupported),
176
+
177
+ missing: [
178
+ ...(tools
179
+ ?.filter(tool => tool.isRequired && !counters[tool.key])
180
+ .map(tool => tool.key) ?? []),
181
+ ...(units
182
+ ?.filter(unit => unit.isRequired && !unitIDs.has(unit.id))
183
+ .map(unit => unit.id) ?? []),
184
+ ],
185
+ };
186
+ };
187
+
188
+ export const unlayerToolsListCustom = (design: UnlayerDesignFormat): UnlayerDesignTool[] => {
189
+ const tools: UnlayerDesignTool[] = [];
190
+
191
+ unlayerToolsIterateCustom(design, tool => {
192
+ tools.push(tool);
193
+ });
194
+
195
+ return tools;
196
+ };
197
+
198
+ export const unlayerToolsBuildDesign = (tools: UnlayerDesignTool[]): UnlayerDesignFormat => {
199
+ const design = unlayerGetDefaultDesign();
200
+
201
+ design.body.rows[0].columns[0].contents = tools;
202
+ return design;
203
+ };
204
+
205
+ export const unlayerUnitsGetIdFromValues = (values?: any): string | undefined =>
206
+ values?.[constGenericsEditor.adminConfigProperty]?.crossId;
207
+ export const unlayerUnitsGetTitleFromValues = (values?: any): string | undefined =>
208
+ values?.[constGenericsEditor.adminConfigProperty]?.title;
209
+
210
+ export const unlayerUnitsGetId = (tool: UnlayerDesignTool): string | undefined =>
211
+ unlayerUnitsGetIdFromValues(tool.values);
212
+ export const unlayerUnitsGetTitle = (tool: UnlayerDesignTool): string | undefined =>
213
+ unlayerUnitsGetTitleFromValues(tool.values);
@@ -0,0 +1,71 @@
1
+ import { UnlayerCoreApi } from './api-core';
2
+ import { SchemaObject } from './shared/schema';
3
+
4
+ export interface UnlayerDesignTool {
5
+ type: string;
6
+ slug: string;
7
+ values: any;
8
+ }
9
+
10
+ export interface UnlayerDesignFormat {
11
+ body: {
12
+ rows: {
13
+ cells: number[];
14
+ columns: {
15
+ contents: UnlayerDesignTool[];
16
+ values?: any;
17
+ }[];
18
+ values?: any;
19
+ }[];
20
+ values?: any;
21
+ };
22
+ counters: Record<string, number>;
23
+ schemaVersion: number;
24
+ }
25
+
26
+ export interface UnlayerExport {
27
+ html: string;
28
+ chunks: any;
29
+ design: UnlayerDesignFormat;
30
+ }
31
+
32
+ export interface UnlayerRef {
33
+ loadDesign(design: any): void;
34
+ saveDesign(cb: (design: any) => void): void;
35
+ exportHtml(cb: (data: UnlayerExport) => void): void;
36
+ }
37
+
38
+ export interface UnlayerEditorMergeTagInfo {
39
+ id: string;
40
+ name: string;
41
+ propertyPath: string;
42
+ }
43
+
44
+ export interface UnlayerEditorUnit {
45
+ id: string;
46
+ generic: string;
47
+ title: string;
48
+ values: any;
49
+ }
50
+
51
+ export interface UnlayerEditorCustomTool {
52
+ key: string;
53
+ title: string;
54
+ path: string;
55
+ }
56
+
57
+ export interface CreateUnlayerEditorProps {
58
+ tools: UnlayerEditorCustomTool[];
59
+ dummyData?: any;
60
+ schema?: SchemaObject;
61
+ customCSS?: string | string[] | undefined;
62
+ customJS?: string | string[] | undefined;
63
+ mergeTags?: UnlayerEditorMergeTagInfo[] | undefined;
64
+ genericConfigMode?: boolean;
65
+ generics?: true | string[];
66
+ units?: UnlayerEditorUnit[];
67
+ noCoreTools?: boolean;
68
+ latest?: boolean;
69
+ blocks?: boolean;
70
+ coreApi: UnlayerCoreApi;
71
+ }
@@ -0,0 +1,176 @@
1
+ import { constGenericsEditor } from './shared/const';
2
+ import { unlayerSupportedFonts } from './shared/fonts';
3
+ import { CreateUnlayerEditorProps } from './unlayer-interface';
4
+
5
+ export interface UnlayerExport {
6
+ html: string;
7
+ chunks: any;
8
+ design: any;
9
+ }
10
+
11
+ export interface Unlayer {
12
+ addEventListener(event: string, cb: (arg: any) => void): void;
13
+ removeEventListener(event: string, cb: (arg: any) => void): void;
14
+ registerCallback(type: string, cb: (...args: any[]) => void): void;
15
+ unregisterCallback(type: string, cb: (...args: any[]) => void): void;
16
+ registerProvider(type: string, cb: (...args: any[]) => void): void;
17
+ unregisterProvider(type: string, cb: (...args: any[]) => void): void;
18
+ loadDesign(design: any): void;
19
+ saveDesign(callback: (design: any) => void): void;
20
+ exportHtml(callback: (data: UnlayerExport) => void): void;
21
+ setMergeTags(tags: any): void;
22
+ createViewer(opts: { render: (values: any) => string }): any;
23
+ registerTool(tool: any): void;
24
+ setBodyValues(values: any): void;
25
+ }
26
+
27
+ export enum DesignUpdatedEventType {
28
+ ContentAdded = 'content:added',
29
+ ContentMoved = 'content:moved',
30
+ ContentRemoved = 'content:removed',
31
+ ContentModified = 'content:modified',
32
+ RowAdded = 'row:added',
33
+ RowMoved = 'row:moved',
34
+ RowRemoved = 'row:removed',
35
+ RowModified = 'row:modified',
36
+ ColumnAdded = 'column:added',
37
+ ColumnRemoved = 'column:removed',
38
+ ColumnModified = 'column:modified',
39
+ BodyModified = 'body:modified',
40
+ }
41
+
42
+ export interface EventDesignUpdated {
43
+ type: DesignUpdatedEventType;
44
+ item: {
45
+ type: string;
46
+ slug?: string;
47
+ values: any;
48
+ };
49
+ }
50
+
51
+ /**
52
+ * !!! IMPORTANT !!!
53
+ * do not forget to update schema version when updating unlayer version
54
+ * it is important for templates export and should be in sync
55
+ * to know correct schema version, open unlayer editor and check output design ('schema' property)
56
+ */
57
+ export const versions = {
58
+ unlayer: '1.2.9',
59
+ schema: 5,
60
+ };
61
+
62
+ export const createUnlayerEditor = (
63
+ container: HTMLDivElement,
64
+ {
65
+ coreApi,
66
+ customCSS,
67
+ customJS,
68
+ latest,
69
+ mergeTags,
70
+ noCoreTools,
71
+ tools,
72
+ units,
73
+ }: CreateUnlayerEditorProps
74
+ ): Unlayer => {
75
+ const customScripts = [
76
+ ...coreApi.pathsCore,
77
+ ...coreApi.pathsCoreTools,
78
+ 'window.dteStore.sendData("--core-loaded")',
79
+ ...tools.map(tool => tool.path),
80
+ ...(customJS ? (Array.isArray(customJS) ? customJS : [customJS]) : []),
81
+ 'window.dteStore.sendData("--data-loaded")',
82
+ ].filter(js => !!js?.trim());
83
+ const customStyles = [
84
+ coreApi.customCss,
85
+ ...(customCSS ? (Array.isArray(customCSS) ? customCSS : [customCSS]) : []),
86
+ ].filter(css => !!css?.trim());
87
+
88
+ console.log('###unlayer init', customScripts, customStyles);
89
+ /**
90
+ * Unlayer supports only passing id of container, but when we use shadowDOM (MFE),
91
+ * getElementById can't find element in it
92
+ * so making this dirty hack to let unlayer find container in DOM
93
+ */
94
+ const id = container.id;
95
+ const currentFind = document.getElementById;
96
+ document.getElementById = function (elementId: string) {
97
+ if (id === elementId) {
98
+ return container;
99
+ }
100
+
101
+ return currentFind.call(document, id);
102
+ };
103
+
104
+ const acv = constGenericsEditor.adminConfigProperty;
105
+ const unitsConfig = (units ?? []).reduce((out, unit) => {
106
+ out[`custom#${unit.generic}:unit:${unit.id}`] = {
107
+ data: {
108
+ [acv]: unit.values[acv],
109
+ },
110
+ properties: {
111
+ [constGenericsEditor.userConfigProperty]: {
112
+ editor: {
113
+ data: { [acv]: unit.values[acv] },
114
+ },
115
+ },
116
+ [constGenericsEditor.infoDataProperty]: {
117
+ editor: {
118
+ data: { title: unit.title },
119
+ },
120
+ },
121
+ },
122
+ };
123
+ return out;
124
+ }, {} as Record<string, any>);
125
+
126
+ const result = (window as any).unlayer.createEditor({
127
+ displayMode: 'web',
128
+ devices: ['desktop'],
129
+ features: {
130
+ preview: false,
131
+ userUploads: false,
132
+ stockImages: false,
133
+ },
134
+ mergeTags: mergeTags?.reduce((out, tag) => {
135
+ out[tag.id] = { name: tag.name, value: tag.propertyPath };
136
+
137
+ return out;
138
+ }, {} as Record<string, { name: string; value: string }>),
139
+ projectId: 5713,
140
+ version: latest ? undefined : versions.unlayer,
141
+ tools: {
142
+ button: { enabled: false },
143
+ html: { enabled: false },
144
+ menu: { enabled: false },
145
+ form: { enabled: false },
146
+ image: { enabled: false },
147
+ ...(noCoreTools
148
+ ? {
149
+ columns: { enabled: false },
150
+ text: { enabled: false },
151
+ heading: { enabled: false },
152
+ divider: { enabled: false },
153
+ }
154
+ : {}),
155
+ ...unitsConfig,
156
+ },
157
+ editor: {
158
+ autoSelectOnDrop: true,
159
+ },
160
+ customJS: customScripts,
161
+ customCSS: customStyles,
162
+ id,
163
+ fonts: {
164
+ showDefaultFonts: false,
165
+ customFonts: unlayerSupportedFonts,
166
+ },
167
+ tabs: {
168
+ content: {},
169
+ body: {},
170
+ blocks: { enabled: false },
171
+ },
172
+ });
173
+
174
+ document.getElementById = currentFind;
175
+ return result;
176
+ };