@servicetitan/dte-unlayer 0.89.0 → 0.91.0

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 (57) hide show
  1. package/dist/api-core.js +29 -22
  2. package/dist/api-core.js.map +1 -1
  3. package/dist/api-custom-tools.js +35 -42
  4. package/dist/api-custom-tools.js.map +1 -1
  5. package/dist/editor-core-source.d.ts +3 -3
  6. package/dist/editor-core-source.d.ts.map +1 -1
  7. package/dist/editor-core-source.js +4 -4
  8. package/dist/editor-core-source.js.map +1 -1
  9. package/dist/editor-core.js +2 -2
  10. package/dist/editor-core.js.map +1 -1
  11. package/dist/editor.d.ts +1 -0
  12. package/dist/editor.d.ts.map +1 -1
  13. package/dist/editor.js +80 -34
  14. package/dist/editor.js.map +1 -1
  15. package/dist/index.js +2 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/loadScript.d.ts.map +1 -1
  18. package/dist/loadScript.js +7 -6
  19. package/dist/loadScript.js.map +1 -1
  20. package/dist/shared/configs.d.ts +6 -0
  21. package/dist/shared/configs.d.ts.map +1 -0
  22. package/dist/shared/configs.js +7 -0
  23. package/dist/shared/configs.js.map +1 -0
  24. package/dist/shared/const.js +2 -1
  25. package/dist/shared/const.js.map +1 -1
  26. package/dist/shared/edit-icon.js +1 -0
  27. package/dist/shared/edit-icon.js.map +1 -1
  28. package/dist/shared/fonts.js +24 -17
  29. package/dist/shared/fonts.js.map +1 -1
  30. package/dist/shared/schema.d.ts.map +1 -1
  31. package/dist/shared/schema.js +91 -49
  32. package/dist/shared/schema.js.map +1 -1
  33. package/dist/shared/tools.d.ts.map +1 -1
  34. package/dist/shared/tools.js +13 -6
  35. package/dist/shared/tools.js.map +1 -1
  36. package/dist/store.d.ts +8 -5
  37. package/dist/store.d.ts.map +1 -1
  38. package/dist/store.js +302 -314
  39. package/dist/store.js.map +1 -1
  40. package/dist/tools.d.ts.map +1 -1
  41. package/dist/tools.js +120 -112
  42. package/dist/tools.js.map +1 -1
  43. package/dist/unlayer-interface.d.ts +5 -0
  44. package/dist/unlayer-interface.d.ts.map +1 -1
  45. package/dist/unlayer-interface.js +2 -1
  46. package/dist/unlayer-interface.js.map +1 -1
  47. package/dist/unlayer.d.ts +1 -0
  48. package/dist/unlayer.d.ts.map +1 -1
  49. package/dist/unlayer.js +115 -70
  50. package/dist/unlayer.js.map +1 -1
  51. package/package.json +1 -1
  52. package/src/editor-core-source.ts +3 -3
  53. package/src/editor.tsx +8 -0
  54. package/src/shared/configs.ts +5 -0
  55. package/src/store.ts +85 -6
  56. package/src/unlayer-interface.tsx +5 -0
  57. package/src/unlayer.tsx +11 -1
package/src/editor.tsx CHANGED
@@ -26,6 +26,8 @@ export interface UnlayerEditorProps {
26
26
 
27
27
  onImage?(file: File): Promise<{ url: string }>;
28
28
 
29
+ onError?(title: string, description?: string): void;
30
+
29
31
  onMessage?(type: string, data: any): void;
30
32
  }
31
33
 
@@ -88,6 +90,12 @@ export const UnlayerEditor = forwardRef<UnlayerRef, UnlayerEditorProps>((props,
88
90
  return () => store.setOnImage();
89
91
  }, [props.onImage, store]);
90
92
 
93
+ useEffect(() => {
94
+ store.setOnError(props.onError);
95
+
96
+ return () => store.setOnError();
97
+ }, [props.onError, store]);
98
+
91
99
  useEffect(() => {
92
100
  store.setOnMessage(props.onMessage);
93
101
 
@@ -0,0 +1,5 @@
1
+ export const defaultImageValidation = {
2
+ maxWidth: 5000,
3
+ maxHeight: 5000,
4
+ maxFileSize: 10 * 1024 * 1024, // 10mb
5
+ };
package/src/store.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { loadScript } from './loadScript';
2
+ import { defaultImageValidation } from './shared/configs';
2
3
  import { UnlayerEditorTwin, UnlayerEventConfig, UnlayerEventRegister } from './shared/const';
3
4
  import { unlayerToolsParseTwinKey } from './shared/tools';
4
5
  import { unlayerToolsIterate } from './tools';
@@ -46,6 +47,7 @@ export class UnlayerStore {
46
47
  private onSnapshotSaveCB?: (snapshot: UnlayerEditorTwin) => void;
47
48
  private onReadyCB?: () => void;
48
49
  private onImageCB?: (file: File) => Promise<{ url: string }>;
50
+ private onErrorCB?: (title: string, description?: string) => void;
49
51
 
50
52
  constructor(readonly props: CreateUnlayerEditorProps) {
51
53
  this.props.eSignFieldTypes = ['Signature', 'Initials', 'Date Signed', 'Full Name'];
@@ -129,6 +131,10 @@ export class UnlayerStore {
129
131
  this.onImageCB = onImage;
130
132
  };
131
133
 
134
+ setOnError = (onError?: UnlayerStore['onErrorCB']) => {
135
+ this.onErrorCB = onError;
136
+ };
137
+
132
138
  setOnMessage = (onMessage?: UnlayerStore['onMessageCB']) => {
133
139
  this.onMessageCB = onMessage;
134
140
  };
@@ -250,22 +256,95 @@ export class UnlayerStore {
250
256
  data,
251
257
  },
252
258
  },
253
- '*'
259
+ '*',
254
260
  );
255
261
  };
256
262
 
257
- private uploadImage = (data: any, done: (result: any) => void) => {
263
+ private uploadImage = async (
264
+ data: { attachments: File[] },
265
+ done: (result: { progress: number; url?: string }) => void,
266
+ ) => {
258
267
  if (!this.onImageCB) {
259
268
  done({ progress: 100 });
260
269
  throw new Error('image upload is not implemented');
261
270
  }
262
271
 
272
+ if (!data.attachments?.length) {
273
+ return;
274
+ }
275
+
276
+ const file = data.attachments[0];
277
+
278
+ const res = await this.validateImage(file, {
279
+ ...defaultImageValidation,
280
+ ...(this.props.imageValidation ?? {}),
281
+ });
282
+
283
+ if (!res.isValid) {
284
+ this.onErrorCB?.(res.title, res.description);
285
+ return;
286
+ }
287
+
263
288
  done({ progress: 0 });
264
289
 
265
- if (data.attachments?.length) {
266
- this.onImageCB(data.attachments[0])
267
- .catch(() => ({ url: '' }))
268
- .then(({ url }) => done({ progress: 100, url }));
290
+ try {
291
+ const { url } = await this.onImageCB(file);
292
+ done({ progress: 100, url });
293
+ } catch {
294
+ this.onErrorCB?.(
295
+ 'Image upload failed',
296
+ 'Something went wrong while uploading the image. Please try again or select another image.',
297
+ );
298
+ done({ progress: 100 });
299
+ }
300
+ };
301
+
302
+ private validateImage = async (
303
+ file: File,
304
+ imageValidation: { maxFileSize: number; maxWidth: number; maxHeight: number },
305
+ ): Promise<{ isValid: boolean; title: string; description: string }> => {
306
+ const { maxFileSize, maxHeight, maxWidth } = imageValidation;
307
+
308
+ if (file.size > maxFileSize) {
309
+ const maxSizeMB = (maxFileSize / 1024 / 1024).toFixed(2);
310
+ const fileSizeMB = (file.size / 1024 / 1024).toFixed(2);
311
+ return {
312
+ isValid: false,
313
+ title: 'Image size limit reached',
314
+ description: `This image is ${fileSizeMB}MB, which is above the ${maxSizeMB}MB limit. Please upload a smaller file to continue.`,
315
+ };
269
316
  }
317
+
318
+ return new Promise(resolve => {
319
+ const img = new Image();
320
+ const objectUrl = URL.createObjectURL(file);
321
+
322
+ img.onload = () => {
323
+ URL.revokeObjectURL(objectUrl);
324
+ const { height, width } = img;
325
+
326
+ if (width > maxWidth || height > maxHeight) {
327
+ resolve({
328
+ isValid: false,
329
+ title: 'Dimension limit reached',
330
+ description: `These image dimensions (${width}x${height}px) exceed the ${maxWidth}x${maxHeight}px limit. Please resize the image and try again.`,
331
+ });
332
+ } else {
333
+ resolve({ isValid: true, title: '', description: '' });
334
+ }
335
+ };
336
+
337
+ img.onerror = () => {
338
+ URL.revokeObjectURL(objectUrl);
339
+ resolve({
340
+ isValid: false,
341
+ title: 'The image is broken',
342
+ description:
343
+ 'Looks like this image isn’t valid. Please choose another image and try again.',
344
+ });
345
+ };
346
+
347
+ img.src = objectUrl;
348
+ });
270
349
  };
271
350
  }
@@ -79,4 +79,9 @@ export interface CreateUnlayerEditorProps {
79
79
  hideBodyMenuItem?: boolean;
80
80
  enableHTMLEditing?: boolean;
81
81
  enableHTMLEditingReadonly?: boolean;
82
+ imageValidation?: {
83
+ maxWidth?: number;
84
+ maxHeight?: number;
85
+ maxFileSize?: number;
86
+ }
82
87
  }
package/src/unlayer.tsx CHANGED
@@ -94,6 +94,15 @@ export const hideUnlayerBodyMenuItem = () => {
94
94
  `;
95
95
  };
96
96
 
97
+ export const hideMobileTabletPreview = () => `
98
+ [data-key="preview-tablet"],
99
+ [data-key="resolution"],
100
+ [data-key="dark-mode"],
101
+ [data-key="preview-desktop"],
102
+ [data-key="preview-mobile"] {
103
+ display: none !important;
104
+ }
105
+ `;
97
106
  export const hideUnlayerTools = () => {
98
107
  return `
99
108
  .blockbuilder-content-tools {
@@ -165,6 +174,7 @@ export const createUnlayerEditor = (
165
174
  * @TODO this is workaround for hiding all tools from unlayer sidebar.
166
175
  * Will be better to use unlayer functionality for this but it is not implemented yet
167
176
  */
177
+ hideMobileTabletPreview(),
168
178
  hideAllTools ? hideUnlayerTools() : '',
169
179
  hideContentControls ? hideUnlayerContentsControls() : '',
170
180
  hideBodyMenuItem ? hideUnlayerBodyMenuItem() : '',
@@ -239,7 +249,7 @@ export const createUnlayerEditor = (
239
249
  displayMode: 'web',
240
250
  devices: ['desktop'],
241
251
  features: {
242
- preview: false,
252
+ preview: true,
243
253
  userUploads: false,
244
254
  stockImages: false,
245
255
  },