@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
@@ -0,0 +1,79 @@
1
+ import { loadScript } from './loadScript';
2
+ import { UnlayerEditorCustomTool } from './unlayer-interface';
3
+
4
+ export interface UnlayerCustomToolsPackageData {
5
+ packageUrl: string;
6
+ customTools: UnlayerEditorCustomTool[];
7
+ }
8
+
9
+ export class UnlayerCustomToolsApi {
10
+ private dataInternal?: UnlayerCustomToolsPackageData;
11
+ private loaded = new Set<string>();
12
+ private schemas?: Record<string, any>;
13
+
14
+ constructor(private packageUrl: string) {}
15
+
16
+ get data() {
17
+ return this.dataInternal!;
18
+ }
19
+
20
+ async init() {
21
+ await fetch(`${this.packageUrl}/data.json`)
22
+ .then(response =>
23
+ response
24
+ .json()
25
+ .then(data => ({ ...data, packageUrl: response.url.replace('/data.json', '') }))
26
+ )
27
+ .then(({ packageUrl, tools }) => ({
28
+ customTools: tools.map((tool: any) => ({
29
+ key: tool.key,
30
+ title: tool.title,
31
+ path: `${packageUrl}/${tool.path}`,
32
+ })),
33
+ packageUrl,
34
+ }))
35
+ .then(data => {
36
+ this.dataInternal = data;
37
+ });
38
+ }
39
+
40
+ clean() {
41
+ this.dataInternal = undefined;
42
+ this.loaded = new Set();
43
+ this.schemas = undefined;
44
+ }
45
+
46
+ async loadSchemas(): Promise<Record<string, any>> {
47
+ if (this.schemas) {
48
+ return this.schemas;
49
+ }
50
+
51
+ return fetch(`${this.data.packageUrl}/schemas.json`)
52
+ .then(response => response.json())
53
+ .then(schemas => {
54
+ this.schemas = schemas;
55
+
56
+ return schemas;
57
+ });
58
+ }
59
+
60
+ async preloadCustomTools(tools: string[]) {
61
+ if (!this.dataInternal) {
62
+ return;
63
+ }
64
+ const toolsMap = this.data.customTools.reduce((out, t) => {
65
+ out[t.key] = t.path;
66
+
67
+ return out;
68
+ }, {} as Record<string, string>);
69
+
70
+ await Promise.all(
71
+ tools
72
+ .filter(t => !!toolsMap[t] && !this.loaded.has(t))
73
+ .map(t => {
74
+ this.loaded.add(t);
75
+ return loadScript(this.data.packageUrl + '/' + toolsMap[t]).catch(() => null);
76
+ })
77
+ );
78
+ }
79
+ }
package/src/editor.tsx ADDED
@@ -0,0 +1,87 @@
1
+ import {
2
+ CSSProperties,
3
+ forwardRef,
4
+ useEffect,
5
+ useImperativeHandle,
6
+ useMemo,
7
+ useRef,
8
+ useState,
9
+ } from 'react';
10
+ import { UnlayerStore, UnlayerDesignChangeInfo } from './store';
11
+ import { UnlayerRef, CreateUnlayerEditorProps } from './unlayer-interface';
12
+
13
+ export interface UnlayerEditorProps {
14
+ id?: string;
15
+ design?: any;
16
+ opts: CreateUnlayerEditorProps;
17
+ minHeight?: number;
18
+ style?: CSSProperties;
19
+ onReady?(): void;
20
+ onChange?(info: UnlayerDesignChangeInfo): void;
21
+ onImage?(file: File): Promise<{ url: string }>;
22
+ onMessage?(type: string, data: any): void;
23
+ }
24
+
25
+ export const useUnlayerRef = () => useRef<UnlayerRef | null>(null);
26
+
27
+ export const UnlayerEditor = forwardRef<UnlayerRef, UnlayerEditorProps>((props, ref) => {
28
+ const [status, setStatus] = useState(0);
29
+ const containerRef = useRef<HTMLDivElement | null>(null);
30
+ const store = useMemo(
31
+ () => new UnlayerStore(props.opts),
32
+ // eslint-disable-next-line react-hooks/exhaustive-deps
33
+ []
34
+ );
35
+ const isReady = status === 2;
36
+
37
+ useEffect(() => {
38
+ if (containerRef.current) {
39
+ store
40
+ .init(containerRef.current)
41
+ .then(() => setStatus(2))
42
+ .catch(() => setStatus(1));
43
+ }
44
+
45
+ return () => store.destroy();
46
+ }, [store]);
47
+ useImperativeHandle(ref, () => store.unlayerRef, [store]);
48
+
49
+ useEffect(() => {
50
+ if (isReady) {
51
+ store.setDesign(props.design);
52
+ }
53
+ }, [isReady, props.design, store]);
54
+
55
+ useEffect(() => {
56
+ store.setOnChange(props.onChange);
57
+
58
+ return () => store.setOnChange();
59
+ }, [props.onChange, store]);
60
+
61
+ useEffect(() => {
62
+ store.setOnReady(props.onReady);
63
+
64
+ return () => store.setOnReady();
65
+ }, [props.onReady, store]);
66
+
67
+ useEffect(() => {
68
+ store.setOnImage(props.onImage);
69
+
70
+ return () => store.setOnImage();
71
+ }, [props.onImage, store]);
72
+
73
+ useEffect(() => {
74
+ store.setOnMessage(props.onMessage);
75
+
76
+ return () => store.setOnMessage();
77
+ }, [props.onMessage, store]);
78
+
79
+ const { minHeight = 800, style = {} } = props;
80
+
81
+ return (
82
+ <div style={{ minHeight, display: 'flex' }}>
83
+ {status === 1 && <p className="c-red-500">error loading editor</p>}
84
+ <div id={props.id ?? 'editor'} style={style} ref={containerRef} />
85
+ </div>
86
+ );
87
+ });
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ export * from './editor';
2
+ export * from './tools';
3
+ export * from './api-core';
4
+ export * from './api-custom-tools';
5
+ export * from './unlayer-interface';
6
+ export * from './loadScript';
7
+ export * from './shared/const';
8
+ export * from './shared/schema';
9
+ export * from './shared/tools';
10
+ export { UnlayerDesignChangeInfo } from './store';
11
+ export { versions } from './unlayer';
@@ -0,0 +1,27 @@
1
+ const isScriptInjected = (scriptUrl: string) => {
2
+ const scripts = document.querySelectorAll('script');
3
+ let injected = false;
4
+
5
+ scripts.forEach(script => {
6
+ if (script.src.includes(scriptUrl)) {
7
+ injected = true;
8
+ }
9
+ });
10
+
11
+ return injected;
12
+ };
13
+
14
+ export const loadScript = (scriptUrl: string): Promise<void> => {
15
+ return new Promise<void>(resolve => {
16
+ if (isScriptInjected(scriptUrl)) {
17
+ resolve();
18
+ }
19
+
20
+ const embedScript = document.createElement('script');
21
+ embedScript.setAttribute('src', scriptUrl);
22
+ embedScript.onload = () => {
23
+ resolve();
24
+ };
25
+ document.head.appendChild(embedScript);
26
+ });
27
+ };
@@ -0,0 +1,30 @@
1
+ import { SchemaObject } from './schema';
2
+
3
+ export const constGenericsEditor = {
4
+ infoDataProperty: '_info',
5
+ adminConfigProperty: 'adminConfig',
6
+ userConfigProperty: 'config',
7
+ };
8
+
9
+ export interface IUnlayerEditorUnit {
10
+ id: string;
11
+ generic: string;
12
+ title: string;
13
+ values: any;
14
+ }
15
+
16
+ export interface UnlayerEventConfig {
17
+ dummyData?: any;
18
+ schema?: SchemaObject;
19
+ generics?: true | string[];
20
+ genericConfigMode?: boolean;
21
+ units?: IUnlayerEditorUnit[];
22
+ }
23
+
24
+ export interface UnlayerEventRegister {
25
+ units?: IUnlayerEditorUnit[];
26
+ customTools?: {
27
+ key: string;
28
+ title?: string;
29
+ }[];
30
+ }
@@ -0,0 +1,126 @@
1
+ export const unlayerSupportedFonts = [
2
+ {
3
+ label: 'Andale Mono',
4
+ value: 'andale mono,times',
5
+ },
6
+ {
7
+ label: 'Arial',
8
+ value: 'arial,helvetica,sans-serif',
9
+ },
10
+ {
11
+ label: 'Arial Black',
12
+ value: 'arial black,avant garde,arial',
13
+ },
14
+ {
15
+ label: 'Book Antiqua',
16
+ value: 'book antiqua,palatino',
17
+ },
18
+ {
19
+ label: 'Comic Sans MS',
20
+ value: 'comic sans ms,sans-serif',
21
+ },
22
+ {
23
+ label: 'Courier New',
24
+ value: 'courier new,courier',
25
+ },
26
+ {
27
+ label: 'Georgia',
28
+ value: 'georgia,palatino',
29
+ },
30
+ {
31
+ label: 'Helvetica',
32
+ value: 'helvetica,sans-serif',
33
+ },
34
+ {
35
+ label: 'Impact',
36
+ value: 'impact,chicago',
37
+ },
38
+ {
39
+ label: 'Symbol',
40
+ value: 'symbol',
41
+ },
42
+ {
43
+ label: 'Tahoma',
44
+ value: 'tahoma,arial,helvetica,sans-serif',
45
+ },
46
+ {
47
+ label: 'Terminal',
48
+ value: 'terminal,monaco',
49
+ },
50
+ {
51
+ label: 'Times New Roman',
52
+ value: 'times new roman,times',
53
+ },
54
+ {
55
+ label: 'Trebuchet MS',
56
+ value: 'trebuchet ms,geneva',
57
+ },
58
+ {
59
+ label: 'Verdana',
60
+ value: 'verdana,geneva',
61
+ },
62
+ {
63
+ label: 'Lobster Two',
64
+ value: "'Lobster Two',cursive",
65
+ url: 'https://fonts.googleapis.com/css?family=Lobster+Two:400,700',
66
+ },
67
+ {
68
+ label: 'Playfair Display',
69
+ value: "'Playfair Display',serif",
70
+ url: 'https://fonts.googleapis.com/css?family=Playfair+Display:400,700',
71
+ },
72
+ {
73
+ label: 'Rubik',
74
+ value: "'Rubik',sans-serif",
75
+ url: 'https://fonts.googleapis.com/css?family=Rubik:400,700',
76
+ },
77
+ {
78
+ label: 'Source Sans Pro',
79
+ value: "'Source Sans Pro',sans-serif",
80
+ url: 'https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700',
81
+ },
82
+ {
83
+ label: 'Open Sans',
84
+ value: "'Open Sans',sans-serif",
85
+ url: 'https://fonts.googleapis.com/css?family=Open+Sans:400,700',
86
+ },
87
+ {
88
+ label: 'Crimson Text',
89
+ value: "'Crimson Text',serif",
90
+ url: 'https://fonts.googleapis.com/css?family=Crimson+Text:400,700',
91
+ },
92
+ {
93
+ label: 'Montserrat',
94
+ value: "'Montserrat',sans-serif",
95
+ url: 'https://fonts.googleapis.com/css?family=Montserrat:400,700',
96
+ },
97
+ {
98
+ label: 'Old Standard TT',
99
+ value: "'Old Standard TT',serif",
100
+ url: 'https://fonts.googleapis.com/css?family=Old+Standard+TT:400,700',
101
+ },
102
+ {
103
+ label: 'Lato',
104
+ value: "'Lato',sans-serif",
105
+ url: 'https://fonts.googleapis.com/css?family=Lato:400,700',
106
+ },
107
+ {
108
+ label: 'Raleway',
109
+ value: "'Raleway',sans-serif",
110
+ url: 'https://fonts.googleapis.com/css?family=Raleway:400,700',
111
+ },
112
+ {
113
+ label: 'Cabin',
114
+ value: "'Cabin',sans-serif",
115
+ url: 'https://fonts.googleapis.com/css?family=Cabin:400,700',
116
+ },
117
+ {
118
+ label: 'Pacifico',
119
+ value: "'Pacifico',cursive",
120
+ url: 'https://fonts.googleapis.com/css?family=Pacifico',
121
+ },
122
+ // custom DTE fonts
123
+ { label: 'Sofia Pro', value: 'Sofia Pro' },
124
+ { label: 'Nunito Sans', value: 'Nunito Sans' },
125
+ { label: 'Montserrat', value: 'Montserrat' },
126
+ ];
@@ -0,0 +1,190 @@
1
+ export type SchemaNodeAvailableTypes = 'object' | 'array' | 'string';
2
+ export const schemaNodeAvailableTypes = ['object', 'array', 'string'];
3
+
4
+ export type SchemaNodeStringSubTypes = 'string' | 'text' | 'html';
5
+ export const schemaNodeStringSubTypes = ['string', 'text', 'html'];
6
+
7
+ export const schemaIsAvailableType = (type: string): type is SchemaNodeAvailableTypes =>
8
+ schemaNodeAvailableTypes.includes(type);
9
+
10
+ export interface SchemaFieldBaseOptions {
11
+ placeholder?: any;
12
+ }
13
+
14
+ interface SchemaNodeProps {
15
+ title?: string;
16
+ options?: SchemaFieldBaseOptions;
17
+ }
18
+
19
+ export interface SchemaObject extends SchemaNodeProps {
20
+ type: 'object';
21
+ properties: Record<string, SchemaNode>;
22
+ }
23
+
24
+ export interface SchemaArray extends SchemaNodeProps {
25
+ type: 'array';
26
+ items: SchemaNode;
27
+ }
28
+
29
+ export interface SchemaSimpleString extends SchemaNodeProps {
30
+ type: 'string';
31
+ subType?: SchemaNodeStringSubTypes;
32
+ }
33
+
34
+ export type SchemaSimple = SchemaSimpleString;
35
+
36
+ export const schemaArrayRootKey = '[]';
37
+ export type SchemaNode = SchemaObject | SchemaArray | SchemaSimple;
38
+
39
+ export const schemaIsObject = (node?: SchemaNode): node is SchemaObject => node?.type === 'object';
40
+ export const schemaIsArray = (node?: SchemaNode): node is SchemaArray => node?.type === 'array';
41
+ export const schemaIsString = (node?: SchemaNode): node is SchemaSimpleString =>
42
+ node?.type === 'string';
43
+ export const schemaIsStringHtml = (node?: SchemaNode): node is SchemaSimpleString =>
44
+ node?.type === 'string' && node?.subType === 'html';
45
+
46
+ export const schemaFindNode = (schema: SchemaNode, key: string): SchemaNode | undefined => {
47
+ const nodeFinder = (node: SchemaNode, keys: string[]): SchemaNode | undefined => {
48
+ if (!node) {
49
+ return undefined;
50
+ }
51
+
52
+ if (!keys.length) {
53
+ return node;
54
+ }
55
+
56
+ const key = keys.shift();
57
+
58
+ if (!key) {
59
+ return undefined;
60
+ }
61
+
62
+ if (key === schemaArrayRootKey) {
63
+ return schemaIsArray(node) ? nodeFinder(node.items, keys) : undefined;
64
+ }
65
+
66
+ return schemaIsObject(node) ? nodeFinder(node.properties[key], keys) : undefined;
67
+ };
68
+
69
+ return nodeFinder(schema, key.split('.'));
70
+ };
71
+
72
+ export const schemaFindParentNode = (schema: SchemaNode, key: string): SchemaNode | undefined => {
73
+ const keys = key.split('.');
74
+
75
+ keys.pop();
76
+
77
+ return keys.length
78
+ ? schemaFindNode(schema, keys.join('.'))
79
+ : schemaIsObject(schema) && schema.properties[key]
80
+ ? schema
81
+ : undefined;
82
+ };
83
+
84
+ export const schemaGetFieldKey = (fullKey: string): string => fullKey.split('.').pop() ?? '';
85
+
86
+ export const schemaBuildNode = (
87
+ type: SchemaNodeAvailableTypes,
88
+ arrayItemType?: SchemaNodeAvailableTypes
89
+ ): SchemaNode => {
90
+ if (type === 'object') {
91
+ return { type: 'object', properties: {}, options: {} };
92
+ }
93
+ if (type === 'array') {
94
+ return { type: 'array', items: schemaBuildNode(arrayItemType ?? 'object'), options: {} };
95
+ }
96
+
97
+ return { type, options: {} };
98
+ };
99
+
100
+ export interface SchemaMetaField {
101
+ fieldKey: string;
102
+ fullKey: string;
103
+ node: SchemaNode;
104
+ title: string; // node.title or fullTitle
105
+ fullTitle: string; // joined title of all parents and field
106
+ isArrayed: boolean;
107
+ isValue: boolean;
108
+ }
109
+
110
+ export type SchemaMap = Record<string, SchemaMetaField>;
111
+
112
+ export const schemaBuildMap = (schema: SchemaObject): { map: SchemaMap; dummyData: any } => {
113
+ const fieldsMap: Record<string, SchemaMetaField> = {};
114
+ const dummyData: any = {};
115
+
116
+ const processNode = (
117
+ node: SchemaNode,
118
+ nodeKey: string,
119
+ parentNodes: { key: string; title: string }[],
120
+ isArrayed: boolean,
121
+ dummyDataNode: any
122
+ ) => {
123
+ const nodeLabel = node.title ?? nodeKey;
124
+ const isArrayRoot = nodeKey === schemaArrayRootKey;
125
+
126
+ if (nodeKey) {
127
+ const fullKey = [...parentNodes.map(n => n.key), nodeKey].join('.');
128
+ const fullTitle = [...parentNodes.map(n => n.title), nodeLabel].join(' - ');
129
+
130
+ fieldsMap[fullKey] = {
131
+ fieldKey: nodeKey,
132
+ fullKey,
133
+ fullTitle,
134
+ title: node.title ?? fullTitle,
135
+ node,
136
+ isArrayed,
137
+ isValue: !schemaIsObject(node) && !schemaIsArray(node),
138
+ };
139
+ }
140
+
141
+ if (schemaIsObject(node)) {
142
+ const properties = node.properties ?? {};
143
+ const keys = Object.keys(properties ?? {}).sort();
144
+ let dummyDataObject = {};
145
+
146
+ if (isArrayRoot) {
147
+ dummyDataNode.push?.(dummyDataObject);
148
+ } else if (nodeKey) {
149
+ dummyDataNode[nodeKey] = dummyDataObject;
150
+ } else {
151
+ dummyDataObject = dummyDataNode;
152
+ }
153
+
154
+ for (const key of keys) {
155
+ processNode(
156
+ properties[key],
157
+ key,
158
+ nodeKey ? [...parentNodes, { key: nodeKey, title: nodeLabel }] : [],
159
+ isArrayed,
160
+ dummyDataObject
161
+ );
162
+ }
163
+ } else if (schemaIsArray(node)) {
164
+ const items = node.items ?? {};
165
+
166
+ dummyDataNode[nodeKey] = [];
167
+ processNode(
168
+ items,
169
+ schemaArrayRootKey,
170
+ nodeKey ? [...parentNodes, { key: nodeKey, title: nodeLabel }] : [],
171
+ true,
172
+ dummyDataNode[nodeKey]
173
+ );
174
+ } else if (nodeKey) {
175
+ const placeholder = node?.options?.placeholder;
176
+
177
+ if (placeholder !== undefined) {
178
+ if (isArrayRoot) {
179
+ dummyDataNode.push?.(placeholder);
180
+ } else {
181
+ dummyDataNode[nodeKey] = placeholder;
182
+ }
183
+ }
184
+ }
185
+ };
186
+
187
+ processNode(schema, '', [], false, dummyData);
188
+
189
+ return { map: fieldsMap, dummyData };
190
+ };
@@ -0,0 +1,18 @@
1
+ export const unlayerUnitsGenerateToolKey = (unitId: string, unitGeneric: string) =>
2
+ `${unitGeneric}:unit:${unitId}`;
3
+
4
+ export const unlayerUnitsParseToolKey = (
5
+ toolKey: string
6
+ ): { id: string; generic: string } | undefined => {
7
+ const [generic, type, id] = (toolKey ?? '').split(':');
8
+
9
+ if (type === 'unit' && generic && id) {
10
+ return { id, generic };
11
+ }
12
+ };
13
+
14
+ export interface UnlayerStore {
15
+ renderHtml(html: string, data: any): string;
16
+ sendData(type: string, data?: any): void;
17
+ getTool(key: string): { getModel(): object; dataSources(values?: any): string[] } | undefined;
18
+ }