@pobuca/email-builder 1.0.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.
- package/README.md +1 -0
- package/assets/icons/dribbble.png +0 -0
- package/assets/icons/facebook.png +0 -0
- package/assets/icons/github.png +0 -0
- package/assets/icons/instagram.png +0 -0
- package/assets/icons/linkedin.png +0 -0
- package/assets/icons/medium.png +0 -0
- package/assets/icons/pinterest.png +0 -0
- package/assets/icons/snapchat.png +0 -0
- package/assets/icons/soundcloud.png +0 -0
- package/assets/icons/tumblr.png +0 -0
- package/assets/icons/twitter.png +0 -0
- package/assets/icons/vimeo.png +0 -0
- package/assets/icons/web.png +0 -0
- package/assets/icons/xing.png +0 -0
- package/assets/icons/youtube.png +0 -0
- package/bundles/pobuca-email-builder.umd.js +4664 -0
- package/bundles/pobuca-email-builder.umd.js.map +1 -0
- package/bundles/pobuca-email-builder.umd.min.js +2 -0
- package/bundles/pobuca-email-builder.umd.min.js.map +1 -0
- package/esm2015/lib/classes/DefaultEmail.js +53 -0
- package/esm2015/lib/classes/Elements.js +229 -0
- package/esm2015/lib/classes/Structure.js +89 -0
- package/esm2015/lib/components/block/block.component.js +89 -0
- package/esm2015/lib/components/block-settings/block-settings.component.js +53 -0
- package/esm2015/lib/components/builder-container/builder-container.component.js +114 -0
- package/esm2015/lib/components/dialog.component.js +30 -0
- package/esm2015/lib/components/general-settings/general-settings.component.js +25 -0
- package/esm2015/lib/components/import-dialog/import-dialog.component.js +62 -0
- package/esm2015/lib/components/preview.component.js +89 -0
- package/esm2015/lib/components/structure/structure.component.js +163 -0
- package/esm2015/lib/components/structure-settings/structure-settings.component.js +31 -0
- package/esm2015/lib/components/template-list-dialog/template-list-dialog.component.js +45 -0
- package/esm2015/lib/directives/dynamic-component.directive.js +46 -0
- package/esm2015/lib/elements/abstract-block.js +23 -0
- package/esm2015/lib/elements/button/button.component.js +29 -0
- package/esm2015/lib/elements/divider/divider.component.js +30 -0
- package/esm2015/lib/elements/image/image.component.js +54 -0
- package/esm2015/lib/elements/social/social.component.js +42 -0
- package/esm2015/lib/elements/spacer/spacer.component.js +30 -0
- package/esm2015/lib/elements/text-element/text-element.component.js +80 -0
- package/esm2015/lib/groups/align.js +79 -0
- package/esm2015/lib/groups/back-repeat.js +55 -0
- package/esm2015/lib/groups/border.js +102 -0
- package/esm2015/lib/groups/color.js +139 -0
- package/esm2015/lib/groups/direction.js +63 -0
- package/esm2015/lib/groups/font-styles.js +112 -0
- package/esm2015/lib/groups/gaps.js +45 -0
- package/esm2015/lib/groups/line-height.js +68 -0
- package/esm2015/lib/groups/link.js +67 -0
- package/esm2015/lib/groups/margin.js +45 -0
- package/esm2015/lib/groups/padding.js +53 -0
- package/esm2015/lib/groups/upload-image.js +112 -0
- package/esm2015/lib/groups/width-height.js +94 -0
- package/esm2015/lib/interceptors/pb-interceptor.interceptor.js +51 -0
- package/esm2015/lib/interfaces/interfaces.js +13 -0
- package/esm2015/lib/pb-email-builder.component.js +246 -0
- package/esm2015/lib/pb-email-builder.module.js +291 -0
- package/esm2015/lib/pb-email-builder.service.js +134 -0
- package/esm2015/lib/pipes/slugify.pipe.js +39 -0
- package/esm2015/lib/services/internals/pb-email-object-store/pb-email-object-store.service.js +165 -0
- package/esm2015/lib/services/internals/user-interfaces/user-interface.service.js +122 -0
- package/esm2015/lib/services/pb-storage/FreeUsersStorage.class.js +11 -0
- package/esm2015/lib/services/pb-storage/index.js +7 -0
- package/esm2015/lib/services/pb-storage/pb-storage.service.js +79 -0
- package/esm2015/lib/services/user-image-uploader-service/free-users-image-uploader.service.js +26 -0
- package/esm2015/lib/services/user-image-uploader-service/index.js +8 -0
- package/esm2015/lib/services/user-image-uploader-service/paid-users-image-uploader.service.js +30 -0
- package/esm2015/lib/services/user-image-uploader-service/upload-bottom-sheet-dialog/upload-bottom-sheet-dialog.component.js +112 -0
- package/esm2015/lib/services/user-image-uploader-service/upload-image-gallery/upload-image-gallery.component.js +91 -0
- package/esm2015/lib/services/user-image-uploader-service/user-image-uploader.service.js +26 -0
- package/esm2015/lib/services/user-middleware-service/FreeUsersMiddleware.js +31 -0
- package/esm2015/lib/services/user-middleware-service/PaidUsersMiddleware.js +12 -0
- package/esm2015/lib/services/user-middleware-service/index.js +8 -0
- package/esm2015/lib/services/user-middleware-service/pb-middlewares.service.js +195 -0
- package/esm2015/lib/services/user-rest-api-service/free-users-rest-api.service.js +16 -0
- package/esm2015/lib/services/user-rest-api-service/index.js +7 -0
- package/esm2015/lib/services/user-rest-api-service/user-rest-api.service.js +116 -0
- package/esm2015/lib/tokens/private-tokens.js +17 -0
- package/esm2015/lib/tokens/tokens.js +159 -0
- package/esm2015/lib/utils.js +147 -0
- package/esm2015/pobuca-email-builder.js +43 -0
- package/esm2015/public_api.js +26 -0
- package/fesm2015/pobuca-email-builder.js +4335 -0
- package/fesm2015/pobuca-email-builder.js.map +1 -0
- package/lib/classes/DefaultEmail.d.ts +9 -0
- package/lib/classes/Elements.d.ts +62 -0
- package/lib/classes/Structure.d.ts +11 -0
- package/lib/components/block/block.component.d.ts +25 -0
- package/lib/components/block-settings/block-settings.component.d.ts +18 -0
- package/lib/components/builder-container/builder-container.component.d.ts +36 -0
- package/lib/components/dialog.component.d.ts +10 -0
- package/lib/components/general-settings/general-settings.component.d.ts +6 -0
- package/lib/components/import-dialog/import-dialog.component.d.ts +16 -0
- package/lib/components/preview.component.d.ts +18 -0
- package/lib/components/structure/structure.component.d.ts +43 -0
- package/lib/components/structure-settings/structure-settings.component.d.ts +9 -0
- package/lib/components/template-list-dialog/template-list-dialog.component.d.ts +15 -0
- package/lib/directives/dynamic-component.directive.d.ts +13 -0
- package/lib/elements/abstract-block.d.ts +8 -0
- package/lib/elements/button/button.component.d.ts +18 -0
- package/lib/elements/divider/divider.component.d.ts +10 -0
- package/lib/elements/image/image.component.d.ts +19 -0
- package/lib/elements/social/social.component.d.ts +21 -0
- package/lib/elements/spacer/spacer.component.d.ts +5 -0
- package/lib/elements/text-element/text-element.component.d.ts +52 -0
- package/lib/groups/align.d.ts +17 -0
- package/lib/groups/back-repeat.d.ts +8 -0
- package/lib/groups/border.d.ts +13 -0
- package/lib/groups/color.d.ts +26 -0
- package/lib/groups/direction.d.ts +13 -0
- package/lib/groups/font-styles.d.ts +17 -0
- package/lib/groups/gaps.d.ts +7 -0
- package/lib/groups/line-height.d.ts +12 -0
- package/lib/groups/link.d.ts +11 -0
- package/lib/groups/margin.d.ts +8 -0
- package/lib/groups/padding.d.ts +8 -0
- package/lib/groups/upload-image.d.ts +19 -0
- package/lib/groups/width-height.d.ts +19 -0
- package/lib/interceptors/pb-interceptor.interceptor.d.ts +10 -0
- package/lib/interfaces/interfaces.d.ts +422 -0
- package/lib/pb-email-builder.component.d.ts +67 -0
- package/lib/pb-email-builder.module.d.ts +51 -0
- package/lib/pb-email-builder.service.d.ts +83 -0
- package/lib/pipes/slugify.pipe.d.ts +9 -0
- package/lib/services/internals/pb-email-object-store/pb-email-object-store.service.d.ts +50 -0
- package/lib/services/internals/user-interfaces/user-interface.service.d.ts +46 -0
- package/lib/services/pb-storage/FreeUsersStorage.class.d.ts +6 -0
- package/lib/services/pb-storage/index.d.ts +2 -0
- package/lib/services/pb-storage/pb-storage.service.d.ts +54 -0
- package/lib/services/user-image-uploader-service/free-users-image-uploader.service.d.ts +12 -0
- package/lib/services/user-image-uploader-service/index.d.ts +3 -0
- package/lib/services/user-image-uploader-service/paid-users-image-uploader.service.d.ts +14 -0
- package/lib/services/user-image-uploader-service/upload-bottom-sheet-dialog/upload-bottom-sheet-dialog.component.d.ts +28 -0
- package/lib/services/user-image-uploader-service/upload-image-gallery/upload-image-gallery.component.d.ts +27 -0
- package/lib/services/user-image-uploader-service/user-image-uploader.service.d.ts +26 -0
- package/lib/services/user-middleware-service/FreeUsersMiddleware.d.ts +15 -0
- package/lib/services/user-middleware-service/PaidUsersMiddleware.d.ts +7 -0
- package/lib/services/user-middleware-service/index.d.ts +3 -0
- package/lib/services/user-middleware-service/pb-middlewares.service.d.ts +190 -0
- package/lib/services/user-rest-api-service/free-users-rest-api.service.d.ts +10 -0
- package/lib/services/user-rest-api-service/index.d.ts +2 -0
- package/lib/services/user-rest-api-service/user-rest-api.service.d.ts +82 -0
- package/lib/tokens/private-tokens.d.ts +5 -0
- package/lib/tokens/tokens.d.ts +70 -0
- package/lib/utils.d.ts +88 -0
- package/package.json +37 -0
- package/pobuca-email-builder.d.ts +42 -0
- package/pobuca-email-builder.metadata.json +1 -0
- package/public_api.d.ts +16 -0
|
@@ -0,0 +1,4335 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, Injectable, Inject, Component, ChangeDetectionStrategy, ViewChild, ElementRef, ViewEncapsulation, ChangeDetectorRef, Input, HostBinding, HostListener, Pipe, EventEmitter, forwardRef, Renderer2, Output, Directive, ComponentFactoryResolver, ViewContainerRef, inject, NgModule } from '@angular/core';
|
|
3
|
+
import { defer, of, BehaviorSubject, Subject, combineLatest, EMPTY, ReplaySubject, animationFrameScheduler, iif, fromEvent, throwError } from 'rxjs';
|
|
4
|
+
import { map, shareReplay, pluck, withLatestFrom, switchMap, exhaustMap, take, catchError, distinct, distinctUntilKeyChanged, subscribeOn, distinctUntilChanged, tap, filter, finalize, takeUntil, mapTo, debounceTime } from 'rxjs/operators';
|
|
5
|
+
import { moveItemInArray, transferArrayItem, DragDropModule } from '@angular/cdk/drag-drop';
|
|
6
|
+
import { HttpClient, HttpEventType, HttpResponse, HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
|
7
|
+
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
|
8
|
+
import { MatDialogRef, MAT_DIALOG_DATA, MatDialog, MatDialogModule } from '@angular/material/dialog';
|
|
9
|
+
import { MatBottomSheetRef, MatBottomSheet, MatBottomSheetModule } from '@angular/material/bottom-sheet';
|
|
10
|
+
import { DomSanitizer } from '@angular/platform-browser';
|
|
11
|
+
import { FlexLayoutModule } from '@angular/flex-layout';
|
|
12
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
13
|
+
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
|
14
|
+
import { MatDividerModule } from '@angular/material/divider';
|
|
15
|
+
import { MatExpansionModule } from '@angular/material/expansion';
|
|
16
|
+
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
17
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
18
|
+
import { MatInputModule } from '@angular/material/input';
|
|
19
|
+
import { MatListModule } from '@angular/material/list';
|
|
20
|
+
import { MatMenuModule } from '@angular/material/menu';
|
|
21
|
+
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
|
22
|
+
import { MatSelectModule } from '@angular/material/select';
|
|
23
|
+
import { MatSidenavModule } from '@angular/material/sidenav';
|
|
24
|
+
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
|
25
|
+
import { MatSliderModule } from '@angular/material/slider';
|
|
26
|
+
import { MatTabsModule } from '@angular/material/tabs';
|
|
27
|
+
import { MatToolbarModule } from '@angular/material/toolbar';
|
|
28
|
+
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
29
|
+
import { ResizableModule } from 'angular-resizable-element';
|
|
30
|
+
import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
|
|
31
|
+
import { DOCUMENT, CommonModule } from '@angular/common';
|
|
32
|
+
import { ScrollingModule } from '@angular/cdk/scrolling';
|
|
33
|
+
import 'quill';
|
|
34
|
+
import { QuillModule, QUILL_CONFIG_TOKEN } from 'ngx-quill';
|
|
35
|
+
import { ColorChromeModule } from 'ngx-color/chrome';
|
|
36
|
+
import { CdkPortal, PortalModule } from '@angular/cdk/portal';
|
|
37
|
+
import { __awaiter } from 'tslib';
|
|
38
|
+
import '@angular/localize/init';
|
|
39
|
+
|
|
40
|
+
/*
|
|
41
|
+
* Copyright (c) 2024 Pobuca.
|
|
42
|
+
* All rights reserved.
|
|
43
|
+
*/
|
|
44
|
+
/**
|
|
45
|
+
* Template Storage/Cache keys.
|
|
46
|
+
*/
|
|
47
|
+
var ETemplatesStorage;
|
|
48
|
+
(function (ETemplatesStorage) {
|
|
49
|
+
ETemplatesStorage["LATEST_USED"] = "NGB_LATEST_USED_TEMPLATES";
|
|
50
|
+
ETemplatesStorage["STORAGE"] = "NGB_TEMP_TEMPLATES_STORAGE";
|
|
51
|
+
})(ETemplatesStorage || (ETemplatesStorage = {}));
|
|
52
|
+
|
|
53
|
+
/*
|
|
54
|
+
* Copyright (c) 2024 Pobuca.
|
|
55
|
+
* All rights reserved.
|
|
56
|
+
*/
|
|
57
|
+
// import { getDiff } from 'recursive-diff';
|
|
58
|
+
/**
|
|
59
|
+
* An alternative lodash cloneDeep function. Use it on your own risk.
|
|
60
|
+
* @param obj Object to be cloned.
|
|
61
|
+
*/
|
|
62
|
+
function cloneDeep(obj = {}) {
|
|
63
|
+
return JSON.parse(JSON.stringify(obj));
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* An alternative lodash isEqual function. Use it on your own risk.
|
|
67
|
+
* @param x Object to be compared
|
|
68
|
+
* @param y the second Object to be compared
|
|
69
|
+
*/
|
|
70
|
+
function isEqual(x, y) {
|
|
71
|
+
// return !getDiff(x, y).length;
|
|
72
|
+
return JSON.stringify(x) === JSON.stringify(y);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* An alternative lodash defaultsDeep function. Use it on your own risk.
|
|
76
|
+
* @param to The object with default properties
|
|
77
|
+
* @param sources An array of objects with default properties
|
|
78
|
+
*/
|
|
79
|
+
function defaultsDeep(to = {}, ...sources) {
|
|
80
|
+
for (const source of sources) {
|
|
81
|
+
for (const key in source) {
|
|
82
|
+
if (to.hasOwnProperty(key)) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (!Array.isArray(source[key]) && typeof source[key] === 'object') {
|
|
86
|
+
to[key] = defaultsDeep(to[key], source[key]);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
to[key] = source[key];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return to;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Create CSS border styles based on {@link IBorder} object.
|
|
97
|
+
* @param border Border object.
|
|
98
|
+
* @param rule Most likely you won't need it, but for some cases, it can be changed. Default is: `border`.
|
|
99
|
+
*
|
|
100
|
+
* @return CSS Border styles.
|
|
101
|
+
*/
|
|
102
|
+
function createBorder(border, rule = 'border') {
|
|
103
|
+
const { color = '#000000', style = 'solid', width = 4, radius = 0 } = border;
|
|
104
|
+
return {
|
|
105
|
+
[rule]: `${width}px ${style} ${color}`,
|
|
106
|
+
borderRadius: `${radius}px`
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Create CSS padding styles based on {@link IPadding} object.
|
|
111
|
+
* @param padding Padding object.
|
|
112
|
+
* @param rule Most likely you won't need it, but for some cases, it can be changed. Default is: `padding`.
|
|
113
|
+
*/
|
|
114
|
+
function createPadding(padding, rule = 'padding') {
|
|
115
|
+
const { top = 10, right = 25, bottom = 10, left = 25 } = padding;
|
|
116
|
+
return {
|
|
117
|
+
[rule]: `${top}px ${right}px ${bottom}px ${left}px`
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Create CSS margin styles based on {@link IMargin} object.
|
|
122
|
+
* @param margin Margin object.
|
|
123
|
+
*/
|
|
124
|
+
function createMargin(margin) {
|
|
125
|
+
const { top = 0, bottom = 0 } = margin;
|
|
126
|
+
return {
|
|
127
|
+
margin: `${top}px 0 ${bottom}px`
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Create CSS font styles based on {@link IFont} object.
|
|
132
|
+
* @param font Font object.
|
|
133
|
+
*/
|
|
134
|
+
function createFont(font) {
|
|
135
|
+
const { family = '', size = 13, style = 'normal', weight = 'normal' } = font;
|
|
136
|
+
return {
|
|
137
|
+
fontFamily: family,
|
|
138
|
+
fontSize: `${size}px`,
|
|
139
|
+
fontStyle: style,
|
|
140
|
+
fontWeight: weight
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Create CSS line-height styles based on {@link ILineHeight} object.
|
|
145
|
+
* @param lineHeight Line-height object.
|
|
146
|
+
*/
|
|
147
|
+
function createLineHeight(lineHeight) {
|
|
148
|
+
const { value = 22, unit = 'px' } = lineHeight;
|
|
149
|
+
return {
|
|
150
|
+
lineHeight: unit !== 'none' ? `${value}${unit}` : 'normal'
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Create background styles based on {@link IBackground} object.
|
|
155
|
+
* @param background Background object.
|
|
156
|
+
*/
|
|
157
|
+
function createBackground(background) {
|
|
158
|
+
const { url = '', color = 'white', repeat = 'no-repeat' } = background;
|
|
159
|
+
return `${color} ${url && `url(${url})`} ${repeat} top center`;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Create Width or Height styles based on {@link IWidthHeight} object.
|
|
163
|
+
* @param widthHeight Width or Height object.
|
|
164
|
+
*/
|
|
165
|
+
function createWidthHeight(widthHeight) {
|
|
166
|
+
const { value = 100, unit = '%', auto = false } = widthHeight;
|
|
167
|
+
return (auto && 'auto') || (['%', 'px'].indexOf(unit) > -1 && `${value}${unit}`) || unit;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Convert bytes to KB-YB.
|
|
171
|
+
* @param bytes Bytes number.
|
|
172
|
+
* @param decimals How many decimals to be used. Default: 2.
|
|
173
|
+
*/
|
|
174
|
+
function bytesToSize(bytes, decimals = 2) {
|
|
175
|
+
if (bytes === 0) {
|
|
176
|
+
return '0 Bytes';
|
|
177
|
+
}
|
|
178
|
+
const k = 1024;
|
|
179
|
+
const dm = decimals < 0 ? 0 : decimals;
|
|
180
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
|
181
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
182
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + sizes[i];
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Get a Social Icon path.
|
|
186
|
+
* @param path Icon path. Ex: facebook.png
|
|
187
|
+
*/
|
|
188
|
+
function getAssetByPath(path) {
|
|
189
|
+
return `https://www.mailjet.com/images/theme/v1/icons/ico-social/${path}`;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* A simple and powerful function that allows to create an `Observable` only when it needs to be used.
|
|
193
|
+
* @param args Arguments to send to new Observable.
|
|
194
|
+
*/
|
|
195
|
+
function deferOf(...args) {
|
|
196
|
+
return defer(() => of(...args));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/*
|
|
200
|
+
* Copyright (c) 2024 Pobuca.
|
|
201
|
+
* All rights reserved.
|
|
202
|
+
*/
|
|
203
|
+
const defaultColumnsOptions = {
|
|
204
|
+
background: {
|
|
205
|
+
color: 'transparent'
|
|
206
|
+
},
|
|
207
|
+
border: {
|
|
208
|
+
width: 0,
|
|
209
|
+
color: '#cccccc',
|
|
210
|
+
radius: 0,
|
|
211
|
+
style: 'solid'
|
|
212
|
+
},
|
|
213
|
+
verticalAlign: 'top'
|
|
214
|
+
};
|
|
215
|
+
class Structure {
|
|
216
|
+
constructor(type = 'cols_1', elements = [], options) {
|
|
217
|
+
this.type = type;
|
|
218
|
+
this.elements = elements;
|
|
219
|
+
this.columns = 1;
|
|
220
|
+
this.id = Date.now();
|
|
221
|
+
this.options = {
|
|
222
|
+
fullWidth: false,
|
|
223
|
+
border: {
|
|
224
|
+
color: '#cccccc',
|
|
225
|
+
style: 'solid',
|
|
226
|
+
width: 0,
|
|
227
|
+
radius: 0
|
|
228
|
+
},
|
|
229
|
+
background: {
|
|
230
|
+
color: '#ffffff',
|
|
231
|
+
url: '',
|
|
232
|
+
repeat: 'repeat',
|
|
233
|
+
size: {
|
|
234
|
+
value: 100,
|
|
235
|
+
unit: 'px',
|
|
236
|
+
auto: true,
|
|
237
|
+
units: ['px', '%', 'cover', 'contain']
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
padding: {
|
|
241
|
+
top: 4,
|
|
242
|
+
right: 4,
|
|
243
|
+
bottom: 4,
|
|
244
|
+
left: 4
|
|
245
|
+
},
|
|
246
|
+
margin: {
|
|
247
|
+
top: 0,
|
|
248
|
+
bottom: 0
|
|
249
|
+
},
|
|
250
|
+
gaps: [4, 4]
|
|
251
|
+
};
|
|
252
|
+
if (!elements.length) {
|
|
253
|
+
if (['cols_2', 'cols_12', 'cols_21'].includes(type)) {
|
|
254
|
+
this.columns = 2;
|
|
255
|
+
}
|
|
256
|
+
else if (type === 'cols_3') {
|
|
257
|
+
this.columns = 3;
|
|
258
|
+
}
|
|
259
|
+
else if (type === 'cols_4') {
|
|
260
|
+
this.columns = 4;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const columns = Array.from({ length: this.columns }, () => defaultColumnsOptions);
|
|
264
|
+
let columnsWidth = [1];
|
|
265
|
+
if (type === 'cols_21') {
|
|
266
|
+
columnsWidth = [4, 6];
|
|
267
|
+
}
|
|
268
|
+
else if (type === 'cols_12') {
|
|
269
|
+
columnsWidth = [6, 4];
|
|
270
|
+
}
|
|
271
|
+
else if (type === 'cols_2') {
|
|
272
|
+
columnsWidth = [5, 5];
|
|
273
|
+
}
|
|
274
|
+
else if (type === 'cols_3') {
|
|
275
|
+
columnsWidth = [3.33, 3.33, 3.33];
|
|
276
|
+
}
|
|
277
|
+
else if (type === 'cols_4') {
|
|
278
|
+
columnsWidth = [2.5, 2.5, 2.5, 2.5];
|
|
279
|
+
}
|
|
280
|
+
this.options = defaultsDeep(options, this.options, {
|
|
281
|
+
columns,
|
|
282
|
+
columnsWidth
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/*
|
|
288
|
+
* Copyright (c) 2024 Pobuca.
|
|
289
|
+
* All rights reserved.
|
|
290
|
+
*/
|
|
291
|
+
class AIPBlock {
|
|
292
|
+
}
|
|
293
|
+
class TextBlock {
|
|
294
|
+
constructor(innerText, options, state = {
|
|
295
|
+
disabled: false,
|
|
296
|
+
message: ''
|
|
297
|
+
}) {
|
|
298
|
+
this.innerText = innerText;
|
|
299
|
+
this.state = state;
|
|
300
|
+
this.type = 'text';
|
|
301
|
+
this.icon = 'text_format';
|
|
302
|
+
this.options = {
|
|
303
|
+
color: '#000000',
|
|
304
|
+
font: {
|
|
305
|
+
fallback: 'Arial, Helvetica, sans-serif',
|
|
306
|
+
family: 'Roboto',
|
|
307
|
+
style: 'normal',
|
|
308
|
+
size: 16,
|
|
309
|
+
weight: 400
|
|
310
|
+
},
|
|
311
|
+
lineHeight: {
|
|
312
|
+
value: 40,
|
|
313
|
+
unit: 'none'
|
|
314
|
+
},
|
|
315
|
+
padding: {
|
|
316
|
+
top: 10,
|
|
317
|
+
right: 25,
|
|
318
|
+
bottom: 10,
|
|
319
|
+
left: 25
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
this.title = $localize `:@@text_block:Text`;
|
|
323
|
+
this.options = defaultsDeep(options, this.options);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
class ImageBlock {
|
|
327
|
+
constructor(src = 'https://via.placeholder.com/600x200?text=CHANGE+ME', options, state = {
|
|
328
|
+
disabled: false,
|
|
329
|
+
message: ''
|
|
330
|
+
}) {
|
|
331
|
+
this.src = src;
|
|
332
|
+
this.state = state;
|
|
333
|
+
this.type = 'image';
|
|
334
|
+
this.icon = 'image';
|
|
335
|
+
this.options = {
|
|
336
|
+
border: {
|
|
337
|
+
color: '#cccccc',
|
|
338
|
+
style: 'solid',
|
|
339
|
+
width: 0,
|
|
340
|
+
radius: 0
|
|
341
|
+
},
|
|
342
|
+
width: {
|
|
343
|
+
value: 100,
|
|
344
|
+
unit: 'px',
|
|
345
|
+
auto: true,
|
|
346
|
+
units: ['px']
|
|
347
|
+
},
|
|
348
|
+
height: {
|
|
349
|
+
value: 100,
|
|
350
|
+
unit: 'px',
|
|
351
|
+
auto: true,
|
|
352
|
+
units: ['px']
|
|
353
|
+
},
|
|
354
|
+
link: {
|
|
355
|
+
href: '',
|
|
356
|
+
target: '_blank'
|
|
357
|
+
},
|
|
358
|
+
align: 'center',
|
|
359
|
+
title: '',
|
|
360
|
+
padding: {
|
|
361
|
+
top: 0,
|
|
362
|
+
right: 0,
|
|
363
|
+
bottom: 0,
|
|
364
|
+
left: 0
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
this.title = $localize `:@@image_block:Image`;
|
|
368
|
+
this.options = defaultsDeep(options, this.options);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
class ButtonBlock {
|
|
372
|
+
constructor(innerText = 'Click on me', options, state = {
|
|
373
|
+
disabled: false,
|
|
374
|
+
message: ''
|
|
375
|
+
}) {
|
|
376
|
+
this.innerText = innerText;
|
|
377
|
+
this.state = state;
|
|
378
|
+
this.type = 'button';
|
|
379
|
+
this.icon = 'radio_button_checked';
|
|
380
|
+
this.options = {
|
|
381
|
+
backgroundColor: '#414141',
|
|
382
|
+
border: {
|
|
383
|
+
color: '#414141',
|
|
384
|
+
style: 'solid',
|
|
385
|
+
width: 0,
|
|
386
|
+
radius: 3
|
|
387
|
+
},
|
|
388
|
+
color: '#ffffff',
|
|
389
|
+
font: {
|
|
390
|
+
fallback: 'Arial, Helvetica, sans-serif',
|
|
391
|
+
family: 'Roboto',
|
|
392
|
+
size: 13,
|
|
393
|
+
style: 'normal',
|
|
394
|
+
weight: 400
|
|
395
|
+
},
|
|
396
|
+
align: 'center',
|
|
397
|
+
fullWidth: false,
|
|
398
|
+
// verticalAlign: 'middle',
|
|
399
|
+
lineHeight: {
|
|
400
|
+
value: 120,
|
|
401
|
+
unit: '%'
|
|
402
|
+
},
|
|
403
|
+
link: {
|
|
404
|
+
href: '',
|
|
405
|
+
target: '_blank'
|
|
406
|
+
},
|
|
407
|
+
innerPadding: {
|
|
408
|
+
top: 10,
|
|
409
|
+
right: 25,
|
|
410
|
+
bottom: 10,
|
|
411
|
+
left: 25
|
|
412
|
+
},
|
|
413
|
+
padding: {
|
|
414
|
+
top: 10,
|
|
415
|
+
right: 25,
|
|
416
|
+
bottom: 10,
|
|
417
|
+
left: 25
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
this.title = $localize `:@@button_block:Button`;
|
|
421
|
+
this.options = defaultsDeep(options, this.options);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
class DividerBlock {
|
|
425
|
+
constructor(options, state = {
|
|
426
|
+
disabled: false,
|
|
427
|
+
message: ''
|
|
428
|
+
}) {
|
|
429
|
+
this.state = state;
|
|
430
|
+
this.type = 'divider';
|
|
431
|
+
this.icon = 'remove';
|
|
432
|
+
this.options = {
|
|
433
|
+
border: {
|
|
434
|
+
color: '#000000',
|
|
435
|
+
style: 'solid',
|
|
436
|
+
width: 4
|
|
437
|
+
},
|
|
438
|
+
padding: {
|
|
439
|
+
top: 10,
|
|
440
|
+
right: 25,
|
|
441
|
+
bottom: 10,
|
|
442
|
+
left: 25
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
this.title = $localize `:@@divider_block:Divider`;
|
|
446
|
+
this.options = defaultsDeep(options, this.options);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
class SpacerBlock {
|
|
450
|
+
constructor(options, state = {
|
|
451
|
+
disabled: false,
|
|
452
|
+
message: ''
|
|
453
|
+
}) {
|
|
454
|
+
this.state = state;
|
|
455
|
+
this.type = 'spacer';
|
|
456
|
+
this.icon = 'vertical_align_center';
|
|
457
|
+
this.title = $localize `:@@spacer_block:Spacer`;
|
|
458
|
+
this.options = {
|
|
459
|
+
height: {
|
|
460
|
+
value: 20,
|
|
461
|
+
unit: 'px',
|
|
462
|
+
units: ['px']
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
this.options = defaultsDeep(options, this.options);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
class SocialBlock {
|
|
469
|
+
// https://mjml.io/documentation/#mjml-social
|
|
470
|
+
constructor(networks = [], options, state = {
|
|
471
|
+
disabled: false,
|
|
472
|
+
message: ''
|
|
473
|
+
}) {
|
|
474
|
+
this.networks = networks;
|
|
475
|
+
this.state = state;
|
|
476
|
+
this.type = 'social';
|
|
477
|
+
this.icon = 'share';
|
|
478
|
+
this.title = $localize `:@@share_block:Social`;
|
|
479
|
+
this.options = {
|
|
480
|
+
align: 'center',
|
|
481
|
+
mode: 'horizontal',
|
|
482
|
+
font: {
|
|
483
|
+
fallback: 'Arial, Helvetica, sans-serif',
|
|
484
|
+
family: 'Roboto',
|
|
485
|
+
style: 'normal',
|
|
486
|
+
size: 16,
|
|
487
|
+
weight: 400
|
|
488
|
+
},
|
|
489
|
+
iconSize: {
|
|
490
|
+
value: 30,
|
|
491
|
+
unit: 'px'
|
|
492
|
+
},
|
|
493
|
+
lineHeight: {
|
|
494
|
+
value: 16,
|
|
495
|
+
unit: 'px'
|
|
496
|
+
},
|
|
497
|
+
color: '#333333',
|
|
498
|
+
innerPadding: {
|
|
499
|
+
top: 4,
|
|
500
|
+
right: 4,
|
|
501
|
+
bottom: 4,
|
|
502
|
+
left: 4
|
|
503
|
+
},
|
|
504
|
+
padding: {
|
|
505
|
+
top: 10,
|
|
506
|
+
right: 25,
|
|
507
|
+
bottom: 10,
|
|
508
|
+
left: 25
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
this.options = defaultsDeep(options, this.options);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/*
|
|
516
|
+
* Copyright (c) 2024 Pobuca.
|
|
517
|
+
* All rights reserved.
|
|
518
|
+
*/
|
|
519
|
+
/**
|
|
520
|
+
* Some top documentation
|
|
521
|
+
*/
|
|
522
|
+
/**
|
|
523
|
+
* The default social networks list provided by builder. This list is injected by {@link PB_SOCIAL_NETWORKS}.
|
|
524
|
+
*/
|
|
525
|
+
const PB_DEFAULT_SOCIAL_NETWORKS = [
|
|
526
|
+
'github',
|
|
527
|
+
'instagram',
|
|
528
|
+
'web',
|
|
529
|
+
'snapchat',
|
|
530
|
+
'youtube',
|
|
531
|
+
'vimeo',
|
|
532
|
+
'medium',
|
|
533
|
+
'soundcloud',
|
|
534
|
+
'dribbble',
|
|
535
|
+
'facebook',
|
|
536
|
+
'twitter',
|
|
537
|
+
'pinterest',
|
|
538
|
+
'linkedin',
|
|
539
|
+
'tumblr',
|
|
540
|
+
'xing',
|
|
541
|
+
'tiktok'
|
|
542
|
+
];
|
|
543
|
+
/**
|
|
544
|
+
* The token that injects [default social networks list]{@link PB_DEFAULT_SOCIAL_NETWORKS}.
|
|
545
|
+
* Use it to add or remove social networks within list.
|
|
546
|
+
*
|
|
547
|
+
* @example
|
|
548
|
+
* {provide: PB_SOCIAL_NETWORKS, useValue: PB_DEFAULT_SOCIAL_NETWORKS.filter(network => !['github', 'instagram'].includes(network)}
|
|
549
|
+
*/
|
|
550
|
+
const PB_SOCIAL_NETWORKS = new InjectionToken('PB Social Networks', {
|
|
551
|
+
providedIn: 'any',
|
|
552
|
+
factory: () => PB_DEFAULT_SOCIAL_NETWORKS
|
|
553
|
+
});
|
|
554
|
+
/**
|
|
555
|
+
* The default [Google Fonts]{@link https://fonts.google.com} list provided by builder.
|
|
556
|
+
* This list is injected by {@link PB_GOOGLE_FONTS} token.
|
|
557
|
+
*/
|
|
558
|
+
const PB_DEFAULT_GOOGLE_FONTS = [
|
|
559
|
+
'Open+Sans:300,400,500,700',
|
|
560
|
+
'Droid+Sans:300,400,500,700',
|
|
561
|
+
'Lato:300,400,500,700',
|
|
562
|
+
'Roboto:300,400,700,900',
|
|
563
|
+
'Ubuntu:300,400,500,700',
|
|
564
|
+
'Fira+Sans:300,400,500,700',
|
|
565
|
+
'Mansalva:400'
|
|
566
|
+
];
|
|
567
|
+
/**
|
|
568
|
+
* The token that injects [default google fonts]{@link PB_DEFAULT_GOOGLE_FONTS} list.
|
|
569
|
+
*/
|
|
570
|
+
const PB_GOOGLE_FONTS = new InjectionToken('PB Google Fonts', {
|
|
571
|
+
providedIn: 'any',
|
|
572
|
+
factory: () => PB_DEFAULT_GOOGLE_FONTS
|
|
573
|
+
});
|
|
574
|
+
/**
|
|
575
|
+
* The default Fallback Fonts list provided by builder. This list is injected by {@link PB_FALLBACK_FONTS} token.
|
|
576
|
+
*
|
|
577
|
+
* A fallback font is a font that takes effect in case a [Google Font]{@link PB_DEFAULT_GOOGLE_FONTS} can't be loaded by some email
|
|
578
|
+
* providers.
|
|
579
|
+
*/
|
|
580
|
+
const PB_DEFAULT_FALLBACK_FONTS = [
|
|
581
|
+
'Palatino Linotype, Book Antiqua, Palatino, serif',
|
|
582
|
+
'Times New Roman, Times, serif',
|
|
583
|
+
'Arial, Helvetica, sans-serif',
|
|
584
|
+
'Arial Black, Gadget, sans-serif',
|
|
585
|
+
'Comic Sans MS, cursive, sans-serif',
|
|
586
|
+
'Impact, Charcoal, sans-serif',
|
|
587
|
+
'Lucida Sans Unicode, Lucida Grande, sans-serif',
|
|
588
|
+
'Tahoma, Geneva, sans-serif',
|
|
589
|
+
'Trebuchet MS, Helvetica, sans-serif',
|
|
590
|
+
'Verdana, Geneva, sans-serif',
|
|
591
|
+
'Courier New, Courier, monospace',
|
|
592
|
+
'Lucida Console, Monaco, monospace'
|
|
593
|
+
];
|
|
594
|
+
/**
|
|
595
|
+
* The token that injects {@link PB_DEFAULT_FALLBACK_FONTS} list.
|
|
596
|
+
*/
|
|
597
|
+
const PB_FALLBACK_FONTS = new InjectionToken('PB Fallback Fonts', {
|
|
598
|
+
providedIn: 'any',
|
|
599
|
+
factory: () => PB_DEFAULT_FALLBACK_FONTS
|
|
600
|
+
});
|
|
601
|
+
/**
|
|
602
|
+
* The default [Structures]{@link IStructure} list. This list is injected by {@link PB_STRUCTURES} token.
|
|
603
|
+
*/
|
|
604
|
+
const PB_DEFAULT_STRUCTURES = [
|
|
605
|
+
new Structure('cols_1'),
|
|
606
|
+
new Structure('cols_2'),
|
|
607
|
+
new Structure('cols_3'),
|
|
608
|
+
new Structure('cols_4'),
|
|
609
|
+
new Structure('cols_12'),
|
|
610
|
+
new Structure('cols_21')
|
|
611
|
+
];
|
|
612
|
+
/**
|
|
613
|
+
* The token that injects {@link PB_DEFAULT_STRUCTURES} list.
|
|
614
|
+
*/
|
|
615
|
+
const PB_STRUCTURES = new InjectionToken('PB Structures', {
|
|
616
|
+
providedIn: 'any',
|
|
617
|
+
factory: () => PB_DEFAULT_STRUCTURES
|
|
618
|
+
});
|
|
619
|
+
/**
|
|
620
|
+
* The default [Blocks]{@link TBlocks} list. This list is injected by {@link PB_BLOCKS} token.
|
|
621
|
+
*/
|
|
622
|
+
const PB_DEFAULT_BLOCKS = [
|
|
623
|
+
new TextBlock(),
|
|
624
|
+
new ImageBlock(),
|
|
625
|
+
new ButtonBlock(),
|
|
626
|
+
new DividerBlock(),
|
|
627
|
+
new SpacerBlock(),
|
|
628
|
+
new SocialBlock()
|
|
629
|
+
];
|
|
630
|
+
/**
|
|
631
|
+
* The token that injects {@link PB_BLOCKS} list.
|
|
632
|
+
*/
|
|
633
|
+
const PB_BLOCKS = new InjectionToken('PB Blocks', {
|
|
634
|
+
providedIn: 'any',
|
|
635
|
+
factory: () => PB_DEFAULT_BLOCKS
|
|
636
|
+
});
|
|
637
|
+
/**
|
|
638
|
+
* Default builder configuration, injected by {@link PB_CONFIG} token. See {@link IForRootConf} for more info.
|
|
639
|
+
*/
|
|
640
|
+
const PB_DEFAULT_CONFIG = Object.freeze({
|
|
641
|
+
xApiKey: 't7HdQfZjGp6R96fOV4P8v18ggf6LLTQZ1puUI2tz',
|
|
642
|
+
// apiPath: 'https://ngb-api.wlocalhost.org/v1',
|
|
643
|
+
useSaveButton: true,
|
|
644
|
+
usePreviewButton: true,
|
|
645
|
+
useDownloadButton: true,
|
|
646
|
+
templateListIfEmpty: true,
|
|
647
|
+
uploadImageName: 'image'
|
|
648
|
+
});
|
|
649
|
+
/**
|
|
650
|
+
* The token that injects [default builder configurations]{@link PB_DEFAULT_CONFIG}.
|
|
651
|
+
*/
|
|
652
|
+
const PB_CONFIG = new InjectionToken('PB Builder Configuration', {
|
|
653
|
+
providedIn: 'root',
|
|
654
|
+
factory: () => PB_DEFAULT_CONFIG
|
|
655
|
+
});
|
|
656
|
+
/**
|
|
657
|
+
* The default Storage used by builder. It can be either `localStorage` or `sessionStorage`.
|
|
658
|
+
*/
|
|
659
|
+
const PB_STORAGE_FACTORY = new InjectionToken('PB Storage factory token', {
|
|
660
|
+
providedIn: 'any',
|
|
661
|
+
factory: () => sessionStorage
|
|
662
|
+
});
|
|
663
|
+
/**
|
|
664
|
+
* The default storage to cache the templates list. It can be either `localStorage` or `sessionStorage`.
|
|
665
|
+
*/
|
|
666
|
+
const PB_TEMPLATES_TEMPORARY_STORAGE = new InjectionToken('PB Token that stores temporary templates cache', {
|
|
667
|
+
providedIn: 'any',
|
|
668
|
+
factory: () => sessionStorage
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
/*
|
|
672
|
+
* Copyright (c) 2024 Pobuca.
|
|
673
|
+
* All rights reserved.
|
|
674
|
+
*/
|
|
675
|
+
// import { Structure } from './Structure';
|
|
676
|
+
class Defaults {
|
|
677
|
+
constructor(structures = [], general = {
|
|
678
|
+
name: '',
|
|
679
|
+
previewText: '',
|
|
680
|
+
width: {
|
|
681
|
+
value: 600,
|
|
682
|
+
unit: 'px',
|
|
683
|
+
units: ['px']
|
|
684
|
+
},
|
|
685
|
+
background: {
|
|
686
|
+
// url: '',
|
|
687
|
+
color: '#f1f1f1',
|
|
688
|
+
// repeat: 'repeat',
|
|
689
|
+
size: {
|
|
690
|
+
value: 100,
|
|
691
|
+
unit: '%',
|
|
692
|
+
auto: true,
|
|
693
|
+
units: ['px', '%', 'cover', 'contain']
|
|
694
|
+
}
|
|
695
|
+
},
|
|
696
|
+
padding: {
|
|
697
|
+
top: 16,
|
|
698
|
+
right: 10,
|
|
699
|
+
bottom: 10,
|
|
700
|
+
left: 10
|
|
701
|
+
},
|
|
702
|
+
direction: 'ltr',
|
|
703
|
+
global: {
|
|
704
|
+
// TODO Add more global configurations
|
|
705
|
+
// fonts: [],
|
|
706
|
+
padding: {
|
|
707
|
+
top: 0,
|
|
708
|
+
right: 0,
|
|
709
|
+
bottom: 0,
|
|
710
|
+
left: 0
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}) {
|
|
714
|
+
this.structures = structures;
|
|
715
|
+
this.general = general;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
class PBEmail extends Defaults {
|
|
719
|
+
constructor({ structures, general } = {}) {
|
|
720
|
+
super(structures, general);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/*
|
|
725
|
+
* Copyright (c) 2024 Pobuca.
|
|
726
|
+
* All rights reserved.
|
|
727
|
+
*/
|
|
728
|
+
/**
|
|
729
|
+
* This service allows you to control all your users' abilities within this builder. Lets say you want to implement user Roles or just
|
|
730
|
+
* need to disable some functionality for certain users based on purchase plan or on something else. You can allow or disallow almost
|
|
731
|
+
* everything.
|
|
732
|
+
*
|
|
733
|
+
* All the methods must return an Observable, so, in my opinion, that's the best part of this service, you can either return an
|
|
734
|
+
* [EMPTY Observable]{@link https://rxjs.dev/api/index/const/EMPTY}, a modified object or an (Info Modal).pipe(mapTo(EMPTY)) - I think you
|
|
735
|
+
* got the idea.
|
|
736
|
+
*
|
|
737
|
+
* I recommend always using a Modal or a Toast for forbidden actions.
|
|
738
|
+
*
|
|
739
|
+
* It won't work if you have a Regular/Free License - you'll get an error on running the project,
|
|
740
|
+
* you need either an [Extended or Commercial License]{@link https://wlocalhost.org} key for this purpose.
|
|
741
|
+
*
|
|
742
|
+
* @example
|
|
743
|
+
* // Create a custom service
|
|
744
|
+
* class YourOwnMiddlewareService extends PbUserMiddlewaresService {}
|
|
745
|
+
*
|
|
746
|
+
* // Rewrite it into AppModule
|
|
747
|
+
* { provide: PbUserMiddlewaresService, useClass: YourOwnMiddlewareService }
|
|
748
|
+
*/
|
|
749
|
+
class PbUserMiddlewaresService {
|
|
750
|
+
/**
|
|
751
|
+
* This method is called before sending the request to MJML Convertor. Use it to add some additional blocks or anything else to
|
|
752
|
+
* the PBEmail object.
|
|
753
|
+
* @param email PBEmail object that's being sent to MJML Convertor.
|
|
754
|
+
*/
|
|
755
|
+
createHTMLTemplate(email) {
|
|
756
|
+
return deferOf(email);
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Allow or Disallow preview window.
|
|
760
|
+
* @param state Coming Preview state
|
|
761
|
+
*/
|
|
762
|
+
togglePreview(state) {
|
|
763
|
+
return deferOf(state);
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Allow or Disallow preview devices.
|
|
767
|
+
* @param button Device's button that has been clicked
|
|
768
|
+
*/
|
|
769
|
+
togglePreviewDevice(button) {
|
|
770
|
+
return deferOf(button.value);
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Allow or disallow users to select certain templates based either on category or template object.
|
|
774
|
+
* @param category Template's category user has selected
|
|
775
|
+
* @param template Template user has selected
|
|
776
|
+
*/
|
|
777
|
+
chooseTemplate(category, template) {
|
|
778
|
+
return deferOf({ category, template });
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Allow, disallow or return the same path for all user's uploaded images.
|
|
782
|
+
* @param path Path of uploaded image
|
|
783
|
+
*
|
|
784
|
+
* @TODO Check if this middleware is being called before image has been uploaded
|
|
785
|
+
*/
|
|
786
|
+
uploadImage(path) {
|
|
787
|
+
return deferOf(path);
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Allow or disallow users to add blocks within email structures.
|
|
791
|
+
* @param event Event of dropped block
|
|
792
|
+
* @param column Column that block has been dropped
|
|
793
|
+
*
|
|
794
|
+
* @TODO add parent structure as a parameter
|
|
795
|
+
*/
|
|
796
|
+
addBlock(event, column) {
|
|
797
|
+
return deferOf({ event, column });
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Prevent user to edit blocks within email body.
|
|
801
|
+
* @param block Block that user intends to edit
|
|
802
|
+
*/
|
|
803
|
+
editBlock(block) {
|
|
804
|
+
return deferOf(block);
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Prevent user to remove blocks within email body.
|
|
808
|
+
* @param index Block's Index
|
|
809
|
+
* @param column Block's parent column
|
|
810
|
+
* @param block Block that user intends to remove
|
|
811
|
+
*/
|
|
812
|
+
removeBlock(index, column, block) {
|
|
813
|
+
return deferOf({ index, column });
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Prevent user to duplicate blocks within email body.
|
|
817
|
+
* @param index Block's Index
|
|
818
|
+
* @param column Block's parent column
|
|
819
|
+
* @param block Block that user intends to duplicate
|
|
820
|
+
*/
|
|
821
|
+
duplicateBlock(index, column, block) {
|
|
822
|
+
return deferOf({ index, column, block });
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Disable Drag and Drop certain blocks from Blocks list to Email body.
|
|
826
|
+
* Return TRUE to disable all blocks - no matter on block's data.
|
|
827
|
+
* @param block Block to allow or disallow from being dragged
|
|
828
|
+
*/
|
|
829
|
+
disableBlockDragFromList(block) {
|
|
830
|
+
return deferOf(false);
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Prevent users to sort blocks within email body.
|
|
834
|
+
* @param block Block that user intends to change the order
|
|
835
|
+
*/
|
|
836
|
+
disableBlockDragWithinEmailBody(block) {
|
|
837
|
+
return deferOf(false);
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Prevent user to add new structure.
|
|
841
|
+
* @param event Event of dropped structure
|
|
842
|
+
*/
|
|
843
|
+
addStructure(event) {
|
|
844
|
+
return deferOf(event);
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Prevent user to edit the structure.
|
|
848
|
+
* @param structure Structure that user intends to edit
|
|
849
|
+
*/
|
|
850
|
+
editStructure(structure) {
|
|
851
|
+
return deferOf(structure);
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Prevent user to remove the structure.
|
|
855
|
+
* @param index Index of the structure
|
|
856
|
+
* @param structure Structure that user intends to remove
|
|
857
|
+
*/
|
|
858
|
+
removeStructure(index, structure) {
|
|
859
|
+
return deferOf(index);
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Prevent user to duplicate the structure within email body.
|
|
863
|
+
* @param index Index of structure
|
|
864
|
+
* @param structure Structure that user intends to duplicate
|
|
865
|
+
*/
|
|
866
|
+
duplicateStructure(index, structure) {
|
|
867
|
+
return deferOf(index);
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Disable Drag and Drop certain structures from Structures list to Email body.
|
|
871
|
+
* Return TRUE to disable all structures.
|
|
872
|
+
* @param structure Structure to prevent from being dragged
|
|
873
|
+
*/
|
|
874
|
+
disableStructureDragFromList(structure) {
|
|
875
|
+
return deferOf(false);
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Allow or disallow users to sort structures within email body.
|
|
879
|
+
* @param structure Structure that user intends to change the order
|
|
880
|
+
*/
|
|
881
|
+
disableStructureDragWithinEmailBody(structure) {
|
|
882
|
+
return deferOf(false);
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* I don't know why would you do that, but you can disable OnExit confirm modal.
|
|
886
|
+
* @param event Window [BeforeUnloadEvent]{@link https://developer.mozilla.org/en-US/docs/Web/API/BeforeUnloadEvent}
|
|
887
|
+
*/
|
|
888
|
+
preventWindowExit(event) {
|
|
889
|
+
return deferOf(event);
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Allow or disallow user to export certain types of file
|
|
893
|
+
* @param type Type that user intends to download
|
|
894
|
+
*/
|
|
895
|
+
exportFile(type) {
|
|
896
|
+
return deferOf(type);
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* Allow or disallow user to import certain types of file
|
|
900
|
+
* @param file Web APIs [FILE]{@link https://developer.mozilla.org/en-US/docs/Web/API/File}
|
|
901
|
+
*/
|
|
902
|
+
importFile(file) {
|
|
903
|
+
return deferOf(file);
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Catch all errors within email editor, it might not work 100%, it's still on working process.
|
|
907
|
+
* @param error Throwable Error
|
|
908
|
+
*/
|
|
909
|
+
catchError(error) {
|
|
910
|
+
return deferOf(error);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
PbUserMiddlewaresService.decorators = [
|
|
914
|
+
{ type: Injectable }
|
|
915
|
+
];
|
|
916
|
+
|
|
917
|
+
/*
|
|
918
|
+
* Copyright (c) 2024 Pobuca.
|
|
919
|
+
* All rights reserved.
|
|
920
|
+
*/
|
|
921
|
+
/**
|
|
922
|
+
* Default NGB ROOT API Path
|
|
923
|
+
*/
|
|
924
|
+
const NGB_HOST = 'https://ngb-api.wlocalhost.org/v1';
|
|
925
|
+
/**
|
|
926
|
+
* The abstract service that must extend the user's custom REST API service.
|
|
927
|
+
* Contains all required properties needed within project.
|
|
928
|
+
*
|
|
929
|
+
* Rewrite the default methods/properties to get the desired data.
|
|
930
|
+
* We recommend using `shareReplay()` operator if it's just a simple static data.
|
|
931
|
+
*
|
|
932
|
+
* @example
|
|
933
|
+
* // Create a custom service
|
|
934
|
+
* class YourOwnRestApiService extends PbUserRestApiService {}
|
|
935
|
+
*
|
|
936
|
+
* // Rewrite it into AppModule
|
|
937
|
+
* { provide: PbUserRestApiService, useClass: YourOwnRestApiService }
|
|
938
|
+
*/
|
|
939
|
+
class PbUserRestApiService {
|
|
940
|
+
constructor(http) {
|
|
941
|
+
this.http = http;
|
|
942
|
+
/**
|
|
943
|
+
* Get all user merge fields to inject into text editor.
|
|
944
|
+
*/
|
|
945
|
+
this.getAllUserMergeFields$ = deferOf([]);
|
|
946
|
+
/**
|
|
947
|
+
* Get all Custom Modules to be included into Modules List.
|
|
948
|
+
* We're looking for someone who could create many modules to be included by default, if you feel you can help us, please leave a message!
|
|
949
|
+
*/
|
|
950
|
+
this.getAllUserModules$ = deferOf([]);
|
|
951
|
+
/**
|
|
952
|
+
* Get all Predefined Templates and categories.
|
|
953
|
+
* You can leave it to get all default templates from NGB API
|
|
954
|
+
*/
|
|
955
|
+
this.getAllUserTemplates$ = this.http
|
|
956
|
+
.get(NGB_HOST.concat('/templates'))
|
|
957
|
+
.pipe(map(list => list.map(({ category, templates }) => ({
|
|
958
|
+
category,
|
|
959
|
+
templates: templates.map(title => ({
|
|
960
|
+
thumbPath: `https://ngb-templates.s3.amazonaws.com/${category}-${title}.jpg`,
|
|
961
|
+
title,
|
|
962
|
+
templateData: null
|
|
963
|
+
}))
|
|
964
|
+
}))), shareReplay());
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* This method must return the chosen template from Template Gallery List PBEmail object,
|
|
968
|
+
* see {@link PbUserRestApiService#getAllUserTemplates$|getAllUserTemplates$}.
|
|
969
|
+
*
|
|
970
|
+
* Sometimes it can contain 'templateData' property - in this case, just return it.
|
|
971
|
+
*
|
|
972
|
+
* @param category Selected Category from Template Gallery.
|
|
973
|
+
* @param template Selected Template from Template Gallery.
|
|
974
|
+
*
|
|
975
|
+
* @example
|
|
976
|
+
* if (template.templateData) { return template }
|
|
977
|
+
*/
|
|
978
|
+
getUserTemplateData$(category, template) {
|
|
979
|
+
if (template.templateData) {
|
|
980
|
+
return deferOf(template);
|
|
981
|
+
}
|
|
982
|
+
return this.http
|
|
983
|
+
.get(NGB_HOST.concat('/templates'), {
|
|
984
|
+
params: { category, template: template.title }
|
|
985
|
+
})
|
|
986
|
+
.pipe(map(email => (Object.assign(Object.assign({}, template), { templateData: email }))));
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Handle all uploaded images.
|
|
990
|
+
* FormData 'csrf' is deprecated in favor of [HttpClientXsrfModule]{@link https://angular.io/api/common/http/HttpClientXsrfModule}.
|
|
991
|
+
*
|
|
992
|
+
* @param body A FormData that contains 'image' and 'csrf' data.
|
|
993
|
+
* @param uploadImagePath Define a path where NGB have to send the upload request.
|
|
994
|
+
*/
|
|
995
|
+
userImageUpload$(body, uploadImagePath) {
|
|
996
|
+
return this.http.request('POST', uploadImagePath, {
|
|
997
|
+
body,
|
|
998
|
+
reportProgress: true,
|
|
999
|
+
responseType: 'json',
|
|
1000
|
+
observe: 'events'
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Get all images to include into Image Gallery.
|
|
1005
|
+
*/
|
|
1006
|
+
getUserImages$() {
|
|
1007
|
+
return deferOf([]);
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Rewrite the default request which converts PBEmail to HTML, if you have a paid License,
|
|
1011
|
+
* this method allows you to rewrite the default Convertor API Path to yours.
|
|
1012
|
+
*
|
|
1013
|
+
* You can still use the default Convertor API Path, since you have a valid and active License Key.
|
|
1014
|
+
*
|
|
1015
|
+
* @param emailAndFonts PBEmail plus Google Fonts List object.
|
|
1016
|
+
* @param url Your own Convertor API Path, or leave it empty to use NGB servers.
|
|
1017
|
+
*/
|
|
1018
|
+
createHTMLTemplate$(emailAndFonts, url = NGB_HOST) {
|
|
1019
|
+
return this.http.post(url, emailAndFonts);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
PbUserRestApiService.decorators = [
|
|
1023
|
+
{ type: Injectable }
|
|
1024
|
+
];
|
|
1025
|
+
PbUserRestApiService.ctorParameters = () => [
|
|
1026
|
+
{ type: HttpClient }
|
|
1027
|
+
];
|
|
1028
|
+
|
|
1029
|
+
/*
|
|
1030
|
+
* Copyright (c) 2024 Pobuca.
|
|
1031
|
+
* All rights reserved.
|
|
1032
|
+
*/
|
|
1033
|
+
/**
|
|
1034
|
+
* This service allow you rewrite the default Builder Storage methods. It contains all methods builder needs to cache or store objects.
|
|
1035
|
+
* Alternatively you can rewrite the [PB_TEMPLATES_TEMPORARY_STORAGE]{@link PB_TEMPLATES_TEMPORARY_STORAGE}
|
|
1036
|
+
* or [PB_STORAGE_FACTORY]{@link PB_STORAGE_FACTORY} tokens.
|
|
1037
|
+
*
|
|
1038
|
+
* In next major updates, it can have some limitations for Regular License.
|
|
1039
|
+
*
|
|
1040
|
+
* @example
|
|
1041
|
+
* // Create a custom service
|
|
1042
|
+
* class YourOwnStorageService extends IpStorageService {}
|
|
1043
|
+
*
|
|
1044
|
+
* // Rewrite it into AppModule
|
|
1045
|
+
* { provide: IpStorageService, useClass: YourOwnStorageService }
|
|
1046
|
+
*/
|
|
1047
|
+
class PbStorageService {
|
|
1048
|
+
constructor(
|
|
1049
|
+
/**
|
|
1050
|
+
* Storage for templates defined by [PB_TEMPLATES_TEMPORARY_STORAGE]{@link PB_TEMPLATES_TEMPORARY_STORAGE} token.
|
|
1051
|
+
*/
|
|
1052
|
+
templateStorage) {
|
|
1053
|
+
this.templateStorage = templateStorage;
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* Get the latest used templates end user has chosen from templates list.
|
|
1057
|
+
* @return Latest used templates by end user.
|
|
1058
|
+
*/
|
|
1059
|
+
getLatestUsedTemplates() {
|
|
1060
|
+
return JSON.parse(this.templateStorage.getItem(ETemplatesStorage.LATEST_USED)) || [];
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Add a template to end user's chosen templates list.
|
|
1064
|
+
* @param template Template end user chosen.
|
|
1065
|
+
*/
|
|
1066
|
+
addTemplateToLatestUsed(template) {
|
|
1067
|
+
const latest = this.getLatestUsedTemplates().filter(({ title }) => title !== template.title);
|
|
1068
|
+
latest.unshift(template);
|
|
1069
|
+
this.templateStorage.setItem(ETemplatesStorage.LATEST_USED, JSON.stringify(latest));
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Clear all latest used templates cache.
|
|
1073
|
+
*/
|
|
1074
|
+
clearLatestUsed() {
|
|
1075
|
+
this.templateStorage.removeItem(ETemplatesStorage.LATEST_USED);
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Cache a fully templates list.
|
|
1079
|
+
* @param list Template list to cache
|
|
1080
|
+
*/
|
|
1081
|
+
cacheTemplateList(list) {
|
|
1082
|
+
this.templateStorage.setItem(ETemplatesStorage.STORAGE, JSON.stringify(list));
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Get a cached templates list if exists.
|
|
1086
|
+
* @return Either a cached templates list or an empty array.
|
|
1087
|
+
*/
|
|
1088
|
+
getCachedTemplateList() {
|
|
1089
|
+
return JSON.parse(this.templateStorage.getItem(ETemplatesStorage.STORAGE)) || [];
|
|
1090
|
+
}
|
|
1091
|
+
/**
|
|
1092
|
+
* Clear all templates list cache.
|
|
1093
|
+
*/
|
|
1094
|
+
clearCachedTemplateList() {
|
|
1095
|
+
this.templateStorage.removeItem(ETemplatesStorage.STORAGE);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
PbStorageService.decorators = [
|
|
1099
|
+
{ type: Injectable }
|
|
1100
|
+
];
|
|
1101
|
+
PbStorageService.ctorParameters = () => [
|
|
1102
|
+
{ type: Storage, decorators: [{ type: Inject, args: [PB_TEMPLATES_TEMPORARY_STORAGE,] }] }
|
|
1103
|
+
];
|
|
1104
|
+
|
|
1105
|
+
/*
|
|
1106
|
+
* Copyright (c) 2024 Pobuca.
|
|
1107
|
+
* All rights reserved.
|
|
1108
|
+
*/
|
|
1109
|
+
// https://www.npmjs.com/package/recursive-diff
|
|
1110
|
+
/**
|
|
1111
|
+
* @internal
|
|
1112
|
+
*/
|
|
1113
|
+
class PbEmailObjectStoreService {
|
|
1114
|
+
constructor(userRestApi, pbStorage, userMiddleware, googleFonts, config) {
|
|
1115
|
+
this.userRestApi = userRestApi;
|
|
1116
|
+
this.pbStorage = pbStorage;
|
|
1117
|
+
this.userMiddleware = userMiddleware;
|
|
1118
|
+
this.googleFonts = googleFonts;
|
|
1119
|
+
this.config = config;
|
|
1120
|
+
this.Email = new PBEmail();
|
|
1121
|
+
this.clonedEmail = cloneDeep(this.Email);
|
|
1122
|
+
this._Email$ = new BehaviorSubject(this.Email);
|
|
1123
|
+
this.emailAsObservable$ = this._Email$.asObservable();
|
|
1124
|
+
this.emailStructuresAsObservable$ = this.emailAsObservable$.pipe(pluck('structures'));
|
|
1125
|
+
this.generalEmailOptionsAsObservable$ = this.emailAsObservable$.pipe(pluck('general'));
|
|
1126
|
+
this.currentEmailHasChanges$ = this.emailAsObservable$.pipe(map(current => !isEqual(current, this.clonedEmail))
|
|
1127
|
+
// shareReplay(),
|
|
1128
|
+
// tap((isDirty) => console.log('IsDirty', isDirty)),
|
|
1129
|
+
);
|
|
1130
|
+
this.builderContainerStyles$ = this.generalEmailOptionsAsObservable$.pipe(map(({ background, padding, direction }) => (Object.assign({ direction, backgroundRepeat: background.repeat, backgroundColor: background.color, backgroundSize: createWidthHeight(background.size), backgroundPosition: 'top center' }, createPadding(padding)))));
|
|
1131
|
+
// public readonly emailBodyWidth$ = this.generalEmailOptionsAsObservable$.pipe(map(({ width }) => createWidthHeight(width)));
|
|
1132
|
+
this._Mjml$ = new BehaviorSubject('');
|
|
1133
|
+
this.mjmlAsObservable$ = this._Mjml$.asObservable();
|
|
1134
|
+
this._Template$ = new BehaviorSubject('');
|
|
1135
|
+
this.templateAsObservable$ = this._Template$.asObservable();
|
|
1136
|
+
this._onTemplateCreated$ = new Subject();
|
|
1137
|
+
this.onTemplateCreated$ = this._onTemplateCreated$.pipe(withLatestFrom(this._Email$, this._Template$, this._Mjml$), map(([_, ...rest]) => rest));
|
|
1138
|
+
}
|
|
1139
|
+
createHTMLTemplate$() {
|
|
1140
|
+
return this.emailAsObservable$.pipe(switchMap(email => {
|
|
1141
|
+
if (!email.structures.length) {
|
|
1142
|
+
throw new Error('Please add some structures and blocks to perform this action.');
|
|
1143
|
+
}
|
|
1144
|
+
return deferOf(email);
|
|
1145
|
+
}), withLatestFrom(combineLatest([this.currentEmailHasChanges$, this.templateAsObservable$])), exhaustMap(([email, [hasChanges, currentHtml]]) => {
|
|
1146
|
+
if (hasChanges || !currentHtml) {
|
|
1147
|
+
return this.userMiddleware.createHTMLTemplate(email).pipe(exhaustMap(em => this.userRestApi.createHTMLTemplate$(Object.assign(Object.assign({}, em), { googleFonts: this.googleFonts }), this.config.apiPath)), map(res => {
|
|
1148
|
+
if (res.errors.length) {
|
|
1149
|
+
const error = res.errors.map(({ message, tagName }) => `${tagName} > ${message}`).join('\n');
|
|
1150
|
+
throw new Error(error);
|
|
1151
|
+
}
|
|
1152
|
+
this._Mjml$.next(res.mjml);
|
|
1153
|
+
this._Template$.next(res.html);
|
|
1154
|
+
this.clonedEmail = cloneDeep(this.Email);
|
|
1155
|
+
this._onTemplateCreated$.next();
|
|
1156
|
+
return res;
|
|
1157
|
+
}));
|
|
1158
|
+
}
|
|
1159
|
+
return combineLatest([this._Mjml$, this._Template$]).pipe(map(([mjml, html]) => ({ html, mjml, errors: [] })));
|
|
1160
|
+
}));
|
|
1161
|
+
}
|
|
1162
|
+
markForCheck() {
|
|
1163
|
+
this._Email$.next(this.Email);
|
|
1164
|
+
if (this.Email.structures.length) {
|
|
1165
|
+
this.pbStorage.addTemplateToLatestUsed(cloneDeep({
|
|
1166
|
+
templateData: this.Email,
|
|
1167
|
+
title: 'last-edited',
|
|
1168
|
+
thumbPath: 'https://via.placeholder.com/200x300?text=LAST+EDITED'
|
|
1169
|
+
}));
|
|
1170
|
+
}
|
|
1171
|
+
// console.log(getDiff(this.clonedEmail, this.Email));
|
|
1172
|
+
}
|
|
1173
|
+
setEmail(newEmail) {
|
|
1174
|
+
if (!newEmail.structures || !newEmail.general) {
|
|
1175
|
+
throw new Error('Injected object is not a valid PBEmail');
|
|
1176
|
+
}
|
|
1177
|
+
this.Email = newEmail;
|
|
1178
|
+
this.reset();
|
|
1179
|
+
}
|
|
1180
|
+
addStructure({ currentIndex = 0, item }) {
|
|
1181
|
+
const { structures = [] } = this.Email;
|
|
1182
|
+
let isModule = true;
|
|
1183
|
+
if (!item.data.elements.length) {
|
|
1184
|
+
isModule = false;
|
|
1185
|
+
item.data.elements = Array.from({ length: item.data.columns }, () => []);
|
|
1186
|
+
}
|
|
1187
|
+
const newStructure = Object.assign(Object.assign({ id: Date.now() }, cloneDeep(item.data)), { isModule });
|
|
1188
|
+
structures.splice(currentIndex, 0, newStructure);
|
|
1189
|
+
this.markForCheck();
|
|
1190
|
+
return newStructure;
|
|
1191
|
+
}
|
|
1192
|
+
changeStructureOrder({ previousIndex = 0, currentIndex = 0 }) {
|
|
1193
|
+
moveItemInArray(this.Email.structures, previousIndex, currentIndex);
|
|
1194
|
+
this.markForCheck();
|
|
1195
|
+
}
|
|
1196
|
+
duplicateStructure(index) {
|
|
1197
|
+
const { structures = [] } = this.Email;
|
|
1198
|
+
const newStructure = cloneDeep(Object.assign(Object.assign({}, structures[index]), { id: Date.now() }));
|
|
1199
|
+
structures.splice(index, 0, newStructure);
|
|
1200
|
+
this.markForCheck();
|
|
1201
|
+
return newStructure;
|
|
1202
|
+
}
|
|
1203
|
+
removeStructure(index) {
|
|
1204
|
+
const { structures = [] } = this.Email;
|
|
1205
|
+
structures.splice(index, 1);
|
|
1206
|
+
this.markForCheck();
|
|
1207
|
+
}
|
|
1208
|
+
addBlock({ previousIndex = 0, currentIndex = 0, item, previousContainer: { id, data } }, column) {
|
|
1209
|
+
// console.log(id);
|
|
1210
|
+
// console.log(item);
|
|
1211
|
+
if (id === 'block-elements') {
|
|
1212
|
+
column.splice(currentIndex, 0, cloneDeep(item.data));
|
|
1213
|
+
}
|
|
1214
|
+
else {
|
|
1215
|
+
transferArrayItem(data, column, previousIndex, currentIndex);
|
|
1216
|
+
}
|
|
1217
|
+
this.markForCheck();
|
|
1218
|
+
}
|
|
1219
|
+
changeBlockOrder({ previousIndex = 0, currentIndex = 0 }, column) {
|
|
1220
|
+
moveItemInArray(column, previousIndex, currentIndex);
|
|
1221
|
+
this.markForCheck();
|
|
1222
|
+
}
|
|
1223
|
+
duplicateBlock(key, column, block) {
|
|
1224
|
+
const newBlock = cloneDeep(block);
|
|
1225
|
+
column.splice(key, 0, newBlock);
|
|
1226
|
+
this.markForCheck();
|
|
1227
|
+
return newBlock;
|
|
1228
|
+
}
|
|
1229
|
+
removeBlock(key, column) {
|
|
1230
|
+
column.splice(key, 1);
|
|
1231
|
+
this.markForCheck();
|
|
1232
|
+
}
|
|
1233
|
+
reset() {
|
|
1234
|
+
this._Mjml$.next(null);
|
|
1235
|
+
this._Template$.next(null);
|
|
1236
|
+
this.clonedEmail = cloneDeep(this.Email);
|
|
1237
|
+
this.markForCheck();
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
PbEmailObjectStoreService.ɵprov = i0.ɵɵdefineInjectable({ factory: function PbEmailObjectStoreService_Factory() { return new PbEmailObjectStoreService(i0.ɵɵinject(PbUserRestApiService), i0.ɵɵinject(PbStorageService), i0.ɵɵinject(PbUserMiddlewaresService), i0.ɵɵinject(PB_GOOGLE_FONTS), i0.ɵɵinject(PB_CONFIG)); }, token: PbEmailObjectStoreService, providedIn: "root" });
|
|
1241
|
+
PbEmailObjectStoreService.decorators = [
|
|
1242
|
+
{ type: Injectable, args: [{
|
|
1243
|
+
providedIn: 'root'
|
|
1244
|
+
},] }
|
|
1245
|
+
];
|
|
1246
|
+
PbEmailObjectStoreService.ctorParameters = () => [
|
|
1247
|
+
{ type: PbUserRestApiService },
|
|
1248
|
+
{ type: PbStorageService },
|
|
1249
|
+
{ type: PbUserMiddlewaresService },
|
|
1250
|
+
{ type: Array, decorators: [{ type: Inject, args: [PB_GOOGLE_FONTS,] }] },
|
|
1251
|
+
{ type: undefined, decorators: [{ type: Inject, args: [PB_CONFIG,] }] }
|
|
1252
|
+
];
|
|
1253
|
+
|
|
1254
|
+
/*
|
|
1255
|
+
* Copyright (c) 2024 Pobuca.
|
|
1256
|
+
* All rights reserved.
|
|
1257
|
+
*/
|
|
1258
|
+
class TemplateListDialogComponent {
|
|
1259
|
+
constructor(matDialogRef, templateList) {
|
|
1260
|
+
this.matDialogRef = matDialogRef;
|
|
1261
|
+
this.templateList = templateList;
|
|
1262
|
+
this.chooseCategory$ = new BehaviorSubject('latest');
|
|
1263
|
+
this.currentCategoryTemplates$ = this.chooseCategory$.pipe(map(category => this.templateList.find(list => list.category === category) || { templates: [] }), pluck('templates'), map(templates => (templates.length > 0 ? templates : null))
|
|
1264
|
+
// map(tmpl => Array.from({ length: 1000 }).map((_, i) => tmpl[0]))
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
chooseTemplate(template) {
|
|
1268
|
+
this.csdTemplate = template;
|
|
1269
|
+
}
|
|
1270
|
+
chageCategory(category) {
|
|
1271
|
+
this.chooseCategory$.next(category);
|
|
1272
|
+
}
|
|
1273
|
+
getTemplatesList() {
|
|
1274
|
+
return this.templateList.filter(({ category }) => category !== 'latest');
|
|
1275
|
+
}
|
|
1276
|
+
startBuilding() {
|
|
1277
|
+
this.chooseCategory$.pipe(take(1)).subscribe(category => {
|
|
1278
|
+
this.matDialogRef.close({ category, template: this.csdTemplate });
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
TemplateListDialogComponent.decorators = [
|
|
1283
|
+
{ type: Component, args: [{
|
|
1284
|
+
selector: 'pb-template-list-dialog',
|
|
1285
|
+
template: "<!--\n ~ Copyright (c) 2024 Pobuca.\n ~ All rights reserved.\n -->\n\n<div fxLayout=\"column\" style=\"height: 100%;\">\n <h2 mat-dialog-title i18n=\"templates|Modal template header\">Custom Templates</h2>\n <div class=\"mat-typography template-list-wrapper\" fxLayout fxLayoutGap=\"1rem\" fxFlex>\n <mat-list fxFlex=\"200px\" dense>\n <mat-list-item (click)=\"chageCategory('latest')\" [ngClass]=\"{active: (chooseCategory$ | async) === 'latest'}\"\n i18n=\"templates|category name\">\n Latest\n </mat-list-item>\n <mat-divider></mat-divider>\n <mat-list-item *ngFor=\"let list of getTemplatesList()\" (click)=\"chageCategory(list.category)\"\n [ngClass]=\"{active: (chooseCategory$ | async) === list.category}\" i18n=\"templates|category name\">\n {{list.category}}\n </mat-list-item>\n </mat-list>\n\n <ng-container *ngIf=\"currentCategoryTemplates$ | async as templates;else emptyCategory\">\n <cdk-virtual-scroll-viewport itemSize=\"150\" [maxBufferPx]=\"150\" fxFlex>\n <div class=\"template-list\">\n <div *cdkVirtualFor=\"let template of templates\" class=\"template-list-item\" (click)=\"chooseTemplate(template)\"\n i18n-matTooltip=\"templates|Click to choose message\" matTooltip=\"Click to choose\">\n <img [src]=\"template.thumbPath\" [alt]=\"template.title\" />\n <div class=\"description\">\n <h3>{{template.title}}</h3>\n </div>\n </div>\n </div>\n </cdk-virtual-scroll-viewport>\n </ng-container>\n </div>\n <mat-dialog-actions align=\"end\">\n <button mat-button mat-dialog-close i18n=\"actions|Undo\">Undo</button>\n <button mat-button (click)=\"startBuilding()\" [disabled]=\"!csdTemplate\" i18n=\"actions|Start\">\n Start\n </button>\n </mat-dialog-actions>\n</div>\n\n\n<ng-template #emptyCategory>\n <div fxFlex fxLayout=\"column\" fxLayoutAlign=\"center center\" class=\"empty-category-notification\">\n <mat-icon>notifications</mat-icon>\n <h2 i18n=\"templates|No templates in current category message\">This category is empty!</h2>\n </div>\n</ng-template>\n",
|
|
1286
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1287
|
+
styles: ["/*!\n * Copyright (c) 2024 Pobuca.\n * All rights reserved.\n */:host{display:block;height:inherit}.template-list-wrapper mat-list{position:sticky;top:0}.template-list-wrapper mat-list mat-list-item{text-transform:capitalize;cursor:pointer;border-radius:3px;transition:all .3s linear}.template-list-wrapper mat-list mat-list-item.active,.template-list-wrapper mat-list mat-list-item:hover{background-color:#f3f3f3}.template-list-wrapper .template-list{display:grid;gap:1rem;grid-template:150px/repeat(3,1fr)}@media screen and (max-width:699px){.template-list-wrapper .template-list{grid-template:150px/repeat(2,1fr)}}.template-list-wrapper .template-list-item{border-radius:3px;overflow:hidden;border:1px solid #ccc;padding:.5rem;position:relative;background-color:#f3f3f3}.template-list-wrapper .template-list-item img{-o-object-fit:cover;object-fit:cover;-o-object-position:top;object-position:top;height:calc(200px - 1rem);width:100%;cursor:pointer;animation:objectPosition 3s linear infinite alternate;animation-play-state:paused}.template-list-wrapper .template-list-item .description{position:absolute;bottom:0;right:0;left:0;background-color:#fff;padding:.5rem;opacity:1;transition:all .3s linear;text-transform:capitalize}.template-list-wrapper .template-list-item .description h3{margin:0}.template-list-wrapper .template-list-item:hover img{animation-play-state:running}.template-list-wrapper .template-list-item:hover .description{bottom:-44px;opacity:.6}.empty-category-notification mat-icon{height:50px;width:50px;font-size:50px}.empty-category-notification h2{text-transform:uppercase}@keyframes objectPosition{to{-o-object-position:bottom;object-position:bottom}}"]
|
|
1288
|
+
},] }
|
|
1289
|
+
];
|
|
1290
|
+
TemplateListDialogComponent.ctorParameters = () => [
|
|
1291
|
+
{ type: MatDialogRef },
|
|
1292
|
+
{ type: Array, decorators: [{ type: Inject, args: [MAT_DIALOG_DATA,] }] }
|
|
1293
|
+
];
|
|
1294
|
+
|
|
1295
|
+
/*
|
|
1296
|
+
* Copyright (c) 2024 Pobuca.
|
|
1297
|
+
* All rights reserved.
|
|
1298
|
+
*/
|
|
1299
|
+
// @dynamic
|
|
1300
|
+
class ConfirmDialogComponent {
|
|
1301
|
+
constructor(dialogRef, data) {
|
|
1302
|
+
this.dialogRef = dialogRef;
|
|
1303
|
+
this.data = data;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
ConfirmDialogComponent.decorators = [
|
|
1307
|
+
{ type: Component, args: [{
|
|
1308
|
+
selector: 'pb-confirm-dialog',
|
|
1309
|
+
template: `
|
|
1310
|
+
<h2 mat-dialog-title>Are you sure?</h2>
|
|
1311
|
+
<mat-dialog-actions fxLayout fxLayoutAlign="space-between center" fxLayoutGap="1rem">
|
|
1312
|
+
<button mat-stroked-button mat-dialog-close="0">No</button>
|
|
1313
|
+
<button mat-stroked-button color="warn" mat-dialog-close="1">Yes</button>
|
|
1314
|
+
</mat-dialog-actions>
|
|
1315
|
+
`
|
|
1316
|
+
},] }
|
|
1317
|
+
];
|
|
1318
|
+
ConfirmDialogComponent.ctorParameters = () => [
|
|
1319
|
+
{ type: MatDialogRef },
|
|
1320
|
+
{ type: undefined, decorators: [{ type: Inject, args: [MAT_DIALOG_DATA,] }] }
|
|
1321
|
+
];
|
|
1322
|
+
|
|
1323
|
+
/*
|
|
1324
|
+
* Copyright (c) 2024 Pobuca.
|
|
1325
|
+
* All rights reserved.
|
|
1326
|
+
*/
|
|
1327
|
+
class ImportDialogComponent {
|
|
1328
|
+
constructor(pbMiddlewares, bottomSheetRef) {
|
|
1329
|
+
this.pbMiddlewares = pbMiddlewares;
|
|
1330
|
+
this.bottomSheetRef = bottomSheetRef;
|
|
1331
|
+
this.importing = false;
|
|
1332
|
+
this.fileReader = new FileReader();
|
|
1333
|
+
}
|
|
1334
|
+
openBrowserModal(event) {
|
|
1335
|
+
event.preventDefault();
|
|
1336
|
+
this.uploadInput.nativeElement.click();
|
|
1337
|
+
}
|
|
1338
|
+
startImporting() {
|
|
1339
|
+
this.importing = true;
|
|
1340
|
+
this.pbMiddlewares
|
|
1341
|
+
.importFile(this.uploadInput.nativeElement.files.item(0))
|
|
1342
|
+
.pipe(take(1), catchError(error => this.pbMiddlewares.catchError(error).pipe(switchMap(() => {
|
|
1343
|
+
this.bottomSheetRef.dismiss(error);
|
|
1344
|
+
return EMPTY;
|
|
1345
|
+
}))))
|
|
1346
|
+
.subscribe(file => {
|
|
1347
|
+
this.fileReader.readAsText(file, 'utf-8');
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
ngOnInit() {
|
|
1351
|
+
this.fileReader.addEventListener('loadend', () => {
|
|
1352
|
+
this.importing = false;
|
|
1353
|
+
try {
|
|
1354
|
+
const email = JSON.parse(this.fileReader.result);
|
|
1355
|
+
this.bottomSheetRef.dismiss(email);
|
|
1356
|
+
}
|
|
1357
|
+
catch (error) {
|
|
1358
|
+
this.bottomSheetRef.dismiss(null);
|
|
1359
|
+
throw new Error(error);
|
|
1360
|
+
}
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
ngOnDestroy() { }
|
|
1364
|
+
}
|
|
1365
|
+
ImportDialogComponent.decorators = [
|
|
1366
|
+
{ type: Component, args: [{
|
|
1367
|
+
selector: 'pb-import-dialog',
|
|
1368
|
+
template: "<!--\n ~ Copyright (c) 2024 Pobuca.\n ~ All rights reserved.\n -->\n\n<mat-nav-list>\n <mat-list-item (click)=\"openBrowserModal($event)\">\n <div *ngIf=\"!importing\">\n <span mat-line i18n=\"import|Import JSON file\">Import JSON file</span>\n </div>\n <ng-container *ngIf=\"importing\">\n <span mat-line i18n=\"import|Importing in progress\">Importing in progress ...</span>\n <!-- <span mat-line>{{ imageInfo }}</span> -->\n </ng-container>\n </mat-list-item>\n <mat-progress-bar *ngIf=\"importing\" mode=\"buffer\" value=\"100\"></mat-progress-bar>\n</mat-nav-list>\n<input type=\"file\" accept=\"application/json\" fxHide #uploadInput (change)=\"startImporting()\" />\n",
|
|
1369
|
+
styles: ["/*!\n * Copyright (c) 2024 Pobuca.\n * All rights reserved.\n */"]
|
|
1370
|
+
},] }
|
|
1371
|
+
];
|
|
1372
|
+
ImportDialogComponent.ctorParameters = () => [
|
|
1373
|
+
{ type: PbUserMiddlewaresService },
|
|
1374
|
+
{ type: MatBottomSheetRef }
|
|
1375
|
+
];
|
|
1376
|
+
ImportDialogComponent.propDecorators = {
|
|
1377
|
+
uploadInput: [{ type: ViewChild, args: ['uploadInput', { read: ElementRef, static: true },] }]
|
|
1378
|
+
};
|
|
1379
|
+
|
|
1380
|
+
/*
|
|
1381
|
+
* Copyright (c) 2024 Pobuca.
|
|
1382
|
+
* All rights reserved.
|
|
1383
|
+
*/
|
|
1384
|
+
/**
|
|
1385
|
+
* @internal
|
|
1386
|
+
*/
|
|
1387
|
+
class PbUserInterfaceService {
|
|
1388
|
+
constructor(emailObjectStore, matDialog, bottomSheet, snackBar) {
|
|
1389
|
+
this.emailObjectStore = emailObjectStore;
|
|
1390
|
+
this.matDialog = matDialog;
|
|
1391
|
+
this.bottomSheet = bottomSheet;
|
|
1392
|
+
this.snackBar = snackBar;
|
|
1393
|
+
this.activeMatProgress$ = new ReplaySubject();
|
|
1394
|
+
this.cdkDropListConnectedTo$ = this.emailObjectStore.emailStructuresAsObservable$.pipe(
|
|
1395
|
+
// tap(structures => console.log('Structures Length', structures.length)),
|
|
1396
|
+
map(structures => structures.reduce((prev, { elements }, structureKey) => {
|
|
1397
|
+
prev.push(...elements.map((_, columnKey) => `column-droplist-${columnKey}-${structureKey}`));
|
|
1398
|
+
return prev;
|
|
1399
|
+
}, [])), distinct(), distinctUntilKeyChanged('length'),
|
|
1400
|
+
// tap(dropList => console.log('Drop List', dropList)),
|
|
1401
|
+
shareReplay());
|
|
1402
|
+
this._currentEditingStructure$ = new BehaviorSubject(null);
|
|
1403
|
+
this.currentEditingStructure$ = this._currentEditingStructure$.pipe(subscribeOn(animationFrameScheduler), distinctUntilChanged(), catchError(() => of(null)),
|
|
1404
|
+
// tap(editStructure => console.log(editStructure)),
|
|
1405
|
+
shareReplay());
|
|
1406
|
+
this._currentEditingBlock$ = new BehaviorSubject(null);
|
|
1407
|
+
this.currentEditingBlock$ = this._currentEditingBlock$.pipe(subscribeOn(animationFrameScheduler), distinctUntilChanged(), catchError(() => of(null)), shareReplay());
|
|
1408
|
+
this._editGeneralSettings$ = new Subject();
|
|
1409
|
+
this.editGeneralSettings$ = this._editGeneralSettings$.pipe(subscribeOn(animationFrameScheduler), tap(() => this.changeTabIndex(2)), distinctUntilChanged(), shareReplay());
|
|
1410
|
+
this._currentTabIndex$ = new BehaviorSubject(0);
|
|
1411
|
+
this.currentTabIndex$ = this._currentTabIndex$.pipe(subscribeOn(animationFrameScheduler), distinctUntilChanged());
|
|
1412
|
+
}
|
|
1413
|
+
editBlock(block) {
|
|
1414
|
+
this._currentEditingStructure$.next(null);
|
|
1415
|
+
this._currentEditingBlock$.next(block);
|
|
1416
|
+
this._editGeneralSettings$.next('block');
|
|
1417
|
+
}
|
|
1418
|
+
editStructure(structure) {
|
|
1419
|
+
this._currentEditingBlock$.next(null);
|
|
1420
|
+
this._editGeneralSettings$.next('structure');
|
|
1421
|
+
this._currentEditingStructure$.next(structure);
|
|
1422
|
+
}
|
|
1423
|
+
editGeneralSettings() {
|
|
1424
|
+
this.resetElements();
|
|
1425
|
+
this._editGeneralSettings$.next('general');
|
|
1426
|
+
}
|
|
1427
|
+
changeTabIndex(index) {
|
|
1428
|
+
this._currentTabIndex$.next(index);
|
|
1429
|
+
}
|
|
1430
|
+
confirmDialog$() {
|
|
1431
|
+
return this.matDialog
|
|
1432
|
+
.open(ConfirmDialogComponent)
|
|
1433
|
+
.afterClosed()
|
|
1434
|
+
.pipe(map(res => res === '1'));
|
|
1435
|
+
}
|
|
1436
|
+
templatesListDialog$(templateList) {
|
|
1437
|
+
return this.matDialog
|
|
1438
|
+
.open(TemplateListDialogComponent, {
|
|
1439
|
+
data: templateList,
|
|
1440
|
+
width: '60vw',
|
|
1441
|
+
maxWidth: '800px',
|
|
1442
|
+
height: '70vh',
|
|
1443
|
+
maxHeight: '900px'
|
|
1444
|
+
})
|
|
1445
|
+
.afterClosed()
|
|
1446
|
+
.pipe(filter(tmpl => Boolean(tmpl)));
|
|
1447
|
+
}
|
|
1448
|
+
importDialog$() {
|
|
1449
|
+
return this.bottomSheet
|
|
1450
|
+
.open(ImportDialogComponent, {
|
|
1451
|
+
ariaLabel: 'Browse media'
|
|
1452
|
+
})
|
|
1453
|
+
.afterDismissed();
|
|
1454
|
+
}
|
|
1455
|
+
notify(msg, close = 'dismiss', duration = 3000) {
|
|
1456
|
+
return this.snackBar.open(msg, close, { duration });
|
|
1457
|
+
}
|
|
1458
|
+
currentEditingBlock() {
|
|
1459
|
+
return this._currentEditingBlock$.getValue();
|
|
1460
|
+
}
|
|
1461
|
+
currentEditingStructure() {
|
|
1462
|
+
return this._currentEditingStructure$.getValue();
|
|
1463
|
+
}
|
|
1464
|
+
currentStructureEqualWith(structure) {
|
|
1465
|
+
return this.currentEditingStructure() === structure;
|
|
1466
|
+
}
|
|
1467
|
+
currentBlockEqualWith(block) {
|
|
1468
|
+
return this.currentEditingBlock() === block;
|
|
1469
|
+
}
|
|
1470
|
+
currentStructureContainsActiveBlock(structure) {
|
|
1471
|
+
return structure.elements.some(elems => elems.some(block => block === this.currentEditingBlock()));
|
|
1472
|
+
}
|
|
1473
|
+
resetElements() {
|
|
1474
|
+
this._currentEditingStructure$.next(null);
|
|
1475
|
+
this._currentEditingBlock$.next(null);
|
|
1476
|
+
}
|
|
1477
|
+
reset() {
|
|
1478
|
+
this.resetElements();
|
|
1479
|
+
this.changeTabIndex(0);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
PbUserInterfaceService.decorators = [
|
|
1483
|
+
{ type: Injectable }
|
|
1484
|
+
];
|
|
1485
|
+
PbUserInterfaceService.ctorParameters = () => [
|
|
1486
|
+
{ type: PbEmailObjectStoreService },
|
|
1487
|
+
{ type: MatDialog },
|
|
1488
|
+
{ type: MatBottomSheet },
|
|
1489
|
+
{ type: MatSnackBar }
|
|
1490
|
+
];
|
|
1491
|
+
|
|
1492
|
+
/*
|
|
1493
|
+
* Copyright (c) 2024 Pobuca.
|
|
1494
|
+
* All rights reserved.
|
|
1495
|
+
*/
|
|
1496
|
+
/**
|
|
1497
|
+
* @deprecated Soon it will be removed
|
|
1498
|
+
* @internal
|
|
1499
|
+
*/
|
|
1500
|
+
class PbEmailBuilderService {
|
|
1501
|
+
constructor(config,
|
|
1502
|
+
// @Inject(PB_GOOGLE_FONTS) private googleFonts: string[],
|
|
1503
|
+
emailObjectStore, pbMiddleWares, pbUserInterface // private _http: HttpClient,
|
|
1504
|
+
) {
|
|
1505
|
+
this.config = config;
|
|
1506
|
+
this.emailObjectStore = emailObjectStore;
|
|
1507
|
+
this.pbMiddleWares = pbMiddleWares;
|
|
1508
|
+
this.pbUserInterface = pbUserInterface;
|
|
1509
|
+
// private _template = new BehaviorSubject<string>('');
|
|
1510
|
+
// private _mjml = new BehaviorSubject<string>('');
|
|
1511
|
+
// private readonly _onSave$ = new Subject();
|
|
1512
|
+
/**
|
|
1513
|
+
* @deprecated Please use the new service "UserRestApiService" instead
|
|
1514
|
+
*/
|
|
1515
|
+
this.modules = new Set();
|
|
1516
|
+
/**
|
|
1517
|
+
* @deprecated Please use the new service "UserRestApiService" instead
|
|
1518
|
+
*/
|
|
1519
|
+
this.customTemplates = new Set();
|
|
1520
|
+
/**
|
|
1521
|
+
* @deprecated Please use the new service "UserRestApiService" instead
|
|
1522
|
+
*/
|
|
1523
|
+
this._mergeTags = new Set();
|
|
1524
|
+
this.onTemplateCreated$ = this.emailObjectStore.onTemplateCreated$.pipe(shareReplay());
|
|
1525
|
+
/**
|
|
1526
|
+
* @deprecated Use onTemplateCreated$ instead
|
|
1527
|
+
*/
|
|
1528
|
+
this.onSave$ = this.onTemplateCreated$;
|
|
1529
|
+
/**
|
|
1530
|
+
* @deprecated Use isLoading$ instead
|
|
1531
|
+
*/
|
|
1532
|
+
this.isLoading = this.pbUserInterface.activeMatProgress$.asObservable();
|
|
1533
|
+
this.isLoading$ = this.pbUserInterface.activeMatProgress$.asObservable();
|
|
1534
|
+
this.config = Object.assign(Object.assign({}, PB_DEFAULT_CONFIG), this.config);
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Save current Email Template
|
|
1538
|
+
*/
|
|
1539
|
+
createHTMLTemplate$() {
|
|
1540
|
+
this.pbUserInterface.activeMatProgress$.next(true);
|
|
1541
|
+
return this.emailObjectStore.createHTMLTemplate$().pipe(tap(() => this.pbUserInterface.activeMatProgress$.next(false)), catchError((error) => this.pbMiddleWares.catchError(error).pipe(switchMap(() => {
|
|
1542
|
+
this.pbUserInterface.notify(error.message);
|
|
1543
|
+
this.pbUserInterface.activeMatProgress$.next(false);
|
|
1544
|
+
return EMPTY;
|
|
1545
|
+
}))), finalize(() => this.pbUserInterface.activeMatProgress$.next(false)));
|
|
1546
|
+
}
|
|
1547
|
+
/**
|
|
1548
|
+
* @deprecated Please use ngb.createHTMLTemplate$() instead
|
|
1549
|
+
*/
|
|
1550
|
+
saveEmail() {
|
|
1551
|
+
return this.createHTMLTemplate$();
|
|
1552
|
+
}
|
|
1553
|
+
/**
|
|
1554
|
+
* Get Email as object
|
|
1555
|
+
*/
|
|
1556
|
+
get Email() {
|
|
1557
|
+
return this.emailObjectStore.Email;
|
|
1558
|
+
}
|
|
1559
|
+
/**
|
|
1560
|
+
* Set Email saved from database or created with new PBEmail()
|
|
1561
|
+
*/
|
|
1562
|
+
set Email(newEmail) {
|
|
1563
|
+
this.emailObjectStore.setEmail(newEmail);
|
|
1564
|
+
}
|
|
1565
|
+
/**
|
|
1566
|
+
* @deprecated Use getTemplateAsObservable$() instead
|
|
1567
|
+
*/
|
|
1568
|
+
get Template() {
|
|
1569
|
+
return null;
|
|
1570
|
+
}
|
|
1571
|
+
/**
|
|
1572
|
+
* Set HTML output
|
|
1573
|
+
*/
|
|
1574
|
+
// set Template(template: string) {
|
|
1575
|
+
// this._template.next(template);
|
|
1576
|
+
// }
|
|
1577
|
+
/**
|
|
1578
|
+
* @deprecated Use getMjmlAsObservable$() instead
|
|
1579
|
+
*/
|
|
1580
|
+
get Mjml() {
|
|
1581
|
+
return null;
|
|
1582
|
+
}
|
|
1583
|
+
/**
|
|
1584
|
+
* Listen Email changes
|
|
1585
|
+
*/
|
|
1586
|
+
getEmailAsObservable$() {
|
|
1587
|
+
return this.emailObjectStore.emailAsObservable$;
|
|
1588
|
+
}
|
|
1589
|
+
/**
|
|
1590
|
+
* Listen Template changes
|
|
1591
|
+
*/
|
|
1592
|
+
getTemplateAsObservable$() {
|
|
1593
|
+
return this.emailObjectStore.templateAsObservable$;
|
|
1594
|
+
}
|
|
1595
|
+
/**
|
|
1596
|
+
* Listen MJML changes
|
|
1597
|
+
*/
|
|
1598
|
+
getMjmlAsObservable$() {
|
|
1599
|
+
return this.emailObjectStore.mjmlAsObservable$;
|
|
1600
|
+
}
|
|
1601
|
+
/**
|
|
1602
|
+
* Listen Email and Template changes
|
|
1603
|
+
* @deprecated Use onTemplateCreated$ instead
|
|
1604
|
+
*/
|
|
1605
|
+
onChanges$() {
|
|
1606
|
+
return this.onTemplateCreated$;
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
PbEmailBuilderService.decorators = [
|
|
1610
|
+
{ type: Injectable }
|
|
1611
|
+
];
|
|
1612
|
+
PbEmailBuilderService.ctorParameters = () => [
|
|
1613
|
+
{ type: undefined, decorators: [{ type: Inject, args: [PB_CONFIG,] }] },
|
|
1614
|
+
{ type: PbEmailObjectStoreService },
|
|
1615
|
+
{ type: PbUserMiddlewaresService },
|
|
1616
|
+
{ type: PbUserInterfaceService }
|
|
1617
|
+
];
|
|
1618
|
+
|
|
1619
|
+
/*
|
|
1620
|
+
* Copyright (c) 2024 Pobuca.
|
|
1621
|
+
* All rights reserved.
|
|
1622
|
+
*/
|
|
1623
|
+
class UploadBottomSheetDialogComponent {
|
|
1624
|
+
constructor(sanitizer, ngb, userRestApi, userInterface, middlewares, bottomSheetRef) {
|
|
1625
|
+
this.sanitizer = sanitizer;
|
|
1626
|
+
this.ngb = ngb;
|
|
1627
|
+
this.userRestApi = userRestApi;
|
|
1628
|
+
this.userInterface = userInterface;
|
|
1629
|
+
this.middlewares = middlewares;
|
|
1630
|
+
this.bottomSheetRef = bottomSheetRef;
|
|
1631
|
+
this.uploading = false;
|
|
1632
|
+
this.progress = new Subject();
|
|
1633
|
+
}
|
|
1634
|
+
get imageInfo() {
|
|
1635
|
+
if (this.choosedImage) {
|
|
1636
|
+
const { lastModified, size } = this.choosedImage;
|
|
1637
|
+
const modTime = new Date(lastModified);
|
|
1638
|
+
return `${modTime.toLocaleString()}, ${bytesToSize(size)}.`;
|
|
1639
|
+
}
|
|
1640
|
+
return '';
|
|
1641
|
+
}
|
|
1642
|
+
previewLink() {
|
|
1643
|
+
return this.sanitizer.bypassSecurityTrustResourceUrl(this.objectUrl);
|
|
1644
|
+
}
|
|
1645
|
+
openBrowserModal(event) {
|
|
1646
|
+
event.preventDefault();
|
|
1647
|
+
const { uploadImagePath } = this.ngb.config;
|
|
1648
|
+
if (!this.uploading && uploadImagePath) {
|
|
1649
|
+
this.uploadInput.nativeElement.click();
|
|
1650
|
+
}
|
|
1651
|
+
else if (!uploadImagePath) {
|
|
1652
|
+
this.userInterface.notify(`Hm ... this isn't a bug, it seems 'uploadImagePath' is empty!`);
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
uploadInputChanged() {
|
|
1656
|
+
const { nativeElement } = this.uploadInput;
|
|
1657
|
+
if (nativeElement.files.length) {
|
|
1658
|
+
this._startUploading(nativeElement.files.item(0));
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
_startUploading(image) {
|
|
1662
|
+
if (this.objectUrl) {
|
|
1663
|
+
URL.revokeObjectURL(this.objectUrl);
|
|
1664
|
+
}
|
|
1665
|
+
this.objectUrl = URL.createObjectURL(image);
|
|
1666
|
+
this.uploading = true;
|
|
1667
|
+
const { uploadImagePath, csrf } = this.ngb.config;
|
|
1668
|
+
const formData = new FormData();
|
|
1669
|
+
if (csrf) {
|
|
1670
|
+
formData.append(csrf.name, csrf.token);
|
|
1671
|
+
}
|
|
1672
|
+
formData.append('image', image);
|
|
1673
|
+
return this.userRestApi
|
|
1674
|
+
.userImageUpload$(formData, uploadImagePath)
|
|
1675
|
+
.pipe(catchError((error) => this.middlewares.catchError(error).pipe(switchMap(() => {
|
|
1676
|
+
this.userInterface.notify(error.message);
|
|
1677
|
+
return EMPTY;
|
|
1678
|
+
}))), finalize(() => this.progress.complete()))
|
|
1679
|
+
.subscribe((event) => {
|
|
1680
|
+
if (event.type === HttpEventType.UploadProgress) {
|
|
1681
|
+
const percentDone = Math.round((100 * event.loaded) / event.total);
|
|
1682
|
+
this.progress.next(percentDone);
|
|
1683
|
+
}
|
|
1684
|
+
else if (event instanceof HttpResponse) {
|
|
1685
|
+
if (!event.body.success) {
|
|
1686
|
+
this.userInterface.notify(event.body.message, 'Dismiss', null);
|
|
1687
|
+
}
|
|
1688
|
+
else {
|
|
1689
|
+
this.bottomSheetRef.dismiss(event.body.path);
|
|
1690
|
+
this.userInterface.notify('Successfully uploaded.', null, 1000);
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
});
|
|
1694
|
+
}
|
|
1695
|
+
ngOnDestroy() {
|
|
1696
|
+
if (this.objectUrl) {
|
|
1697
|
+
URL.revokeObjectURL(this.objectUrl);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
UploadBottomSheetDialogComponent.decorators = [
|
|
1702
|
+
{ type: Component, args: [{
|
|
1703
|
+
selector: 'pb-upload-bottom-sheet-dialog',
|
|
1704
|
+
template: "<!--\n ~ Copyright (c) 2024 Pobuca.\n ~ All rights reserved.\n -->\n\n<mat-nav-list>\n <mat-list-item (click)=\"openBrowserModal($event)\">\n <div *ngIf=\"!uploading\">\n <span mat-line i18n=\"upload|Browse images from computer\">Browse images from computer</span>\n </div>\n <ng-container *ngIf=\"uploading\">\n <img matListAvatar [src]=\"previewLink()\" />\n <span mat-line i18n=\"upload|Uploading Message\">Uploading ...</span>\n <span mat-line>{{ imageInfo }}</span>\n </ng-container>\n </mat-list-item>\n <mat-progress-bar *ngIf=\"uploading\" mode=\"buffer\" [value]=\"progress | async\"></mat-progress-bar>\n</mat-nav-list>\n<input type=\"file\" accept=\"image/*\" fxHide #uploadInput (change)=\"uploadInputChanged()\" />\n",
|
|
1705
|
+
styles: [""]
|
|
1706
|
+
},] }
|
|
1707
|
+
];
|
|
1708
|
+
UploadBottomSheetDialogComponent.ctorParameters = () => [
|
|
1709
|
+
{ type: DomSanitizer },
|
|
1710
|
+
{ type: PbEmailBuilderService },
|
|
1711
|
+
{ type: PbUserRestApiService },
|
|
1712
|
+
{ type: PbUserInterfaceService },
|
|
1713
|
+
{ type: PbUserMiddlewaresService },
|
|
1714
|
+
{ type: MatBottomSheetRef }
|
|
1715
|
+
];
|
|
1716
|
+
UploadBottomSheetDialogComponent.propDecorators = {
|
|
1717
|
+
uploadInput: [{ type: ViewChild, args: ['uploadInput', { static: true },] }]
|
|
1718
|
+
};
|
|
1719
|
+
|
|
1720
|
+
/*
|
|
1721
|
+
* Copyright (c) 2024 Pobuca.
|
|
1722
|
+
* All rights reserved.
|
|
1723
|
+
*/
|
|
1724
|
+
/**
|
|
1725
|
+
* A service that must be extended in case you want to rewrite the default
|
|
1726
|
+
* uploading process. In case you have a Regular License, you'll get a simple upload
|
|
1727
|
+
* service, for any other paid Licenses - you get an Image Gallery.
|
|
1728
|
+
*
|
|
1729
|
+
* It won't work if you have a Regular/Free License - you'll get an error on running the project,
|
|
1730
|
+
* you need either an [Extended or Commercial License]{@link https://wlocalhost.org} key for this purpose.
|
|
1731
|
+
*
|
|
1732
|
+
* @example
|
|
1733
|
+
* // Create a custom service
|
|
1734
|
+
* class YourOwnUploaderService extends PbUserImageUploaderService {}
|
|
1735
|
+
*
|
|
1736
|
+
* // Rewrite it into AppModule
|
|
1737
|
+
* { provide: PbUserImageUploaderService, useClass: YourOwnUploaderService }
|
|
1738
|
+
*/
|
|
1739
|
+
class PbUserImageUploaderService {
|
|
1740
|
+
}
|
|
1741
|
+
PbUserImageUploaderService.decorators = [
|
|
1742
|
+
{ type: Injectable }
|
|
1743
|
+
];
|
|
1744
|
+
|
|
1745
|
+
/*
|
|
1746
|
+
* Copyright (c) 2024 Pobuca.
|
|
1747
|
+
* All rights reserved.
|
|
1748
|
+
*/
|
|
1749
|
+
/**
|
|
1750
|
+
* @internal
|
|
1751
|
+
*/
|
|
1752
|
+
class ImageUploader extends PbUserImageUploaderService {
|
|
1753
|
+
constructor(matBottomSheet, userMiddlewaresService) {
|
|
1754
|
+
super();
|
|
1755
|
+
this.matBottomSheet = matBottomSheet;
|
|
1756
|
+
this.userMiddlewaresService = userMiddlewaresService;
|
|
1757
|
+
}
|
|
1758
|
+
browse$() {
|
|
1759
|
+
return this.matBottomSheet
|
|
1760
|
+
.open(UploadBottomSheetDialogComponent, {
|
|
1761
|
+
ariaLabel: 'Browse media'
|
|
1762
|
+
})
|
|
1763
|
+
.afterDismissed()
|
|
1764
|
+
.pipe(switchMap(path => this.userMiddlewaresService.uploadImage(path)));
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
/*
|
|
1769
|
+
* Copyright (c) 2024 Pobuca.
|
|
1770
|
+
* All rights reserved.
|
|
1771
|
+
*/
|
|
1772
|
+
class UploadImageGalleryComponent {
|
|
1773
|
+
constructor(imageCategoryList, userConfig, matDialogRef, sanitizer, userRestApi, middlewares, userInterface) {
|
|
1774
|
+
this.imageCategoryList = imageCategoryList;
|
|
1775
|
+
this.userConfig = userConfig;
|
|
1776
|
+
this.matDialogRef = matDialogRef;
|
|
1777
|
+
this.sanitizer = sanitizer;
|
|
1778
|
+
this.userRestApi = userRestApi;
|
|
1779
|
+
this.middlewares = middlewares;
|
|
1780
|
+
this.userInterface = userInterface;
|
|
1781
|
+
this.currentMenuItem = 'gallery';
|
|
1782
|
+
}
|
|
1783
|
+
changeCurrentMenuItem(item) {
|
|
1784
|
+
this.currentMenuItem = item;
|
|
1785
|
+
}
|
|
1786
|
+
addImageToTemplate(imagePath = this.choseImage) {
|
|
1787
|
+
this.matDialogRef.close(imagePath);
|
|
1788
|
+
}
|
|
1789
|
+
previewImage({ target }) {
|
|
1790
|
+
this.uploadImageFile = target.files.item(0);
|
|
1791
|
+
if (this.imagePreviewObjectUrl) {
|
|
1792
|
+
URL.revokeObjectURL(this.imagePreviewObjectUrl);
|
|
1793
|
+
}
|
|
1794
|
+
this.imagePreviewObjectUrl = URL.createObjectURL(this.uploadImageFile);
|
|
1795
|
+
this.uploadImagePreview = this.sanitizer.bypassSecurityTrustResourceUrl(this.imagePreviewObjectUrl);
|
|
1796
|
+
}
|
|
1797
|
+
startUploading() {
|
|
1798
|
+
const { csrf, uploadImagePath } = this.userConfig;
|
|
1799
|
+
const formData = new FormData();
|
|
1800
|
+
if (csrf) {
|
|
1801
|
+
formData.append(csrf.name, csrf.token);
|
|
1802
|
+
}
|
|
1803
|
+
formData.append('image', this.uploadImageFile);
|
|
1804
|
+
return this.userRestApi
|
|
1805
|
+
.userImageUpload$(formData, uploadImagePath)
|
|
1806
|
+
.pipe(catchError((error) => this.middlewares.catchError(error).pipe(switchMap(() => {
|
|
1807
|
+
this.userInterface.notify(error.message);
|
|
1808
|
+
return EMPTY;
|
|
1809
|
+
}))))
|
|
1810
|
+
.subscribe((event) => {
|
|
1811
|
+
if (event.type === HttpEventType.UploadProgress) {
|
|
1812
|
+
// const percentDone = Math.round((100 * event.loaded) / event.total);
|
|
1813
|
+
// this.progress.next(percentDone);
|
|
1814
|
+
}
|
|
1815
|
+
else if (event instanceof HttpResponse) {
|
|
1816
|
+
if (!event.body.success) {
|
|
1817
|
+
this.userInterface.notify(event.body.message, 'Dismiss', null);
|
|
1818
|
+
}
|
|
1819
|
+
else {
|
|
1820
|
+
this.addImageToTemplate(event.body.path);
|
|
1821
|
+
this.userInterface.notify('Successfully uploaded.', null, 1000);
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
});
|
|
1825
|
+
}
|
|
1826
|
+
ngOnDestroy() {
|
|
1827
|
+
if (this.imagePreviewObjectUrl) {
|
|
1828
|
+
URL.revokeObjectURL(this.imagePreviewObjectUrl);
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
UploadImageGalleryComponent.decorators = [
|
|
1833
|
+
{ type: Component, args: [{
|
|
1834
|
+
selector: 'pb-upload-image-gallery',
|
|
1835
|
+
template: "<!--\n ~ Copyright (c) 2024 Pobuca.\n ~ All rights reserved.\n -->\n\n<div fxLayout=\"column\" style=\"height: 100%;\">\n <h2 mat-dialog-title i18n=\"image-gallery|Modal Image gallery header\">Image Gallery</h2>\n <div class=\"mat-typography gallery-list-wrapper\" fxLayout fxLayoutGap=\"1rem\" fxFlex>\n <mat-list fxFlex=\"200px\">\n <mat-list-item (click)=\"changeCurrentMenuItem('gallery')\" [class.active]=\"currentMenuItem === 'gallery'\"\n i18n=\"image-gallery|Image list\">\n Gallery Image list\n </mat-list-item>\n <mat-list-item (click)=\"changeCurrentMenuItem('upload')\" [class.active]=\"currentMenuItem === 'upload'\"\n i18n=\"image-gallery|Upload an image\">\n Upload an image\n </mat-list-item>\n </mat-list>\n\n <ng-container *ngIf=\"currentMenuItem === 'gallery'\">\n <ng-container *ngIf=\"imageCategoryList.length;else emptyImageList\">\n <cdk-virtual-scroll-viewport itemSize=\"150\" [minBufferPx]=\"150 * 10\" fxFlex\n class=\"gallery-list\">\n <div *cdkVirtualFor=\"let imagePath of imageCategoryList\" class=\"gallery-list-item\"\n (click)=\"choseImage = imagePath\" i18n-matTooltip=\"image-gallery|Click to choose message\"\n matTooltip=\"Click to choose\">\n <img [src]=\"imagePath\" />\n </div>\n </cdk-virtual-scroll-viewport>\n </ng-container>\n </ng-container>\n <ng-container *ngIf=\"currentMenuItem === 'upload'\">\n <div class=\"upload-image\" fxLayout=\"column\" fxLayoutAlign=\"center center\" fxFlex fxLayoutGap=\"1rem\">\n <label class=\"upload-image-item\" [class.empty]=\"!uploadImagePreview\">\n <div class=\"upload-image-item-overview\" fxLayout=\"column\" fxLayoutAlign=\"center center\">\n <mat-icon>backup</mat-icon>\n </div>\n <img class=\"upload-image-item-preview\" *ngIf=\"uploadImagePreview\" [src]=\"uploadImagePreview\" />\n <input type=\"file\" fxHide pattern=\"image/*\" (change)=\"previewImage($event)\">\n </label>\n <button mat-flat-button color=\"primary\" (click)=\"startUploading()\" i18n=\"image-gallery|Upload and insert into template\">\n Upload and insert into template\n </button>\n </div>\n </ng-container>\n </div>\n <mat-dialog-actions align=\"end\">\n <button mat-button mat-dialog-close i18n=\"actions|Undo\">Cancel</button>\n <button mat-stroked-button color=\"primary\" [disabled]=\"!choseImage\" (click)=\"addImageToTemplate()\"\n i18n=\"actions|Start\">\n Add to template\n </button>\n </mat-dialog-actions>\n</div>\n\n\n<ng-template #emptyImageList>\n <div fxFlex fxLayout=\"column\" fxLayoutAlign=\"center center\" class=\"empty-category-notification\">\n <mat-icon>notifications</mat-icon>\n <h2 i18n=\"image-gallery|Image list is empty message\">Image list is empty!</h2>\n </div>\n</ng-template>\n",
|
|
1836
|
+
styles: ["/*!\n * Copyright (c) 2024 Pobuca.\n * All rights reserved.\n */mat-list{background-color:#fff}mat-list mat-list-item{cursor:pointer}mat-list mat-list-item.active{background-color:#ccc}.gallery-list ::ng-deep .cdk-virtual-scroll-content-wrapper{display:grid;gap:.3rem;grid-template:auto/repeat(4,1fr)}.gallery-list-item{height:120px;background-color:#ccc;cursor:pointer}.gallery-list-item img{width:100%;height:100%;-o-object-fit:contain;object-fit:contain;-o-object-position:center;object-position:center}.upload-image-item{width:80%;height:200px;background-color:#ccc;position:relative;border-radius:3px;padding:1em;text-align:center}.upload-image-item.empty .upload-image-item-overview,.upload-image-item:hover .upload-image-item-overview{opacity:1}.upload-image-item-overview{opacity:0;position:absolute;top:0;bottom:0;left:0;right:0;cursor:pointer;transition:opacity .5s linear}.upload-image-item-overview mat-icon{height:50px;width:50px;font-size:50px}.upload-image-item-preview{max-height:100%}"]
|
|
1837
|
+
},] }
|
|
1838
|
+
];
|
|
1839
|
+
UploadImageGalleryComponent.ctorParameters = () => [
|
|
1840
|
+
{ type: Array, decorators: [{ type: Inject, args: [MAT_DIALOG_DATA,] }] },
|
|
1841
|
+
{ type: undefined, decorators: [{ type: Inject, args: [PB_CONFIG,] }] },
|
|
1842
|
+
{ type: MatDialogRef },
|
|
1843
|
+
{ type: DomSanitizer },
|
|
1844
|
+
{ type: PbUserRestApiService },
|
|
1845
|
+
{ type: PbUserMiddlewaresService },
|
|
1846
|
+
{ type: PbUserInterfaceService }
|
|
1847
|
+
];
|
|
1848
|
+
|
|
1849
|
+
/*
|
|
1850
|
+
* Copyright (c) 2024 Pobuca.
|
|
1851
|
+
* All rights reserved.
|
|
1852
|
+
*/
|
|
1853
|
+
/**
|
|
1854
|
+
* @internal
|
|
1855
|
+
*/
|
|
1856
|
+
class PaidUsersImageUploaderServiceService extends PbUserImageUploaderService {
|
|
1857
|
+
constructor(matDialog, userMiddleware, userRestApi) {
|
|
1858
|
+
super();
|
|
1859
|
+
this.matDialog = matDialog;
|
|
1860
|
+
this.userMiddleware = userMiddleware;
|
|
1861
|
+
this.userRestApi = userRestApi;
|
|
1862
|
+
}
|
|
1863
|
+
browse$() {
|
|
1864
|
+
return this.userRestApi.getUserImages$().pipe(exhaustMap(imageCategoryList => this.matDialog
|
|
1865
|
+
.open(UploadImageGalleryComponent, {
|
|
1866
|
+
data: imageCategoryList,
|
|
1867
|
+
width: '60vw',
|
|
1868
|
+
maxWidth: '800px',
|
|
1869
|
+
height: '60vh',
|
|
1870
|
+
maxHeight: '800px'
|
|
1871
|
+
})
|
|
1872
|
+
.afterClosed()), filter(path => !!path.length), switchMap(path => this.userMiddleware.uploadImage(path)));
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
/*
|
|
1877
|
+
* Copyright (c) 2024 Pobuca.
|
|
1878
|
+
* All rights reserved.
|
|
1879
|
+
*/
|
|
1880
|
+
|
|
1881
|
+
/*
|
|
1882
|
+
* Copyright (c) 2024 Pobuca.
|
|
1883
|
+
* All rights reserved.
|
|
1884
|
+
*/
|
|
1885
|
+
class StructureComponent {
|
|
1886
|
+
constructor(userInterfaceService, emailObjectStore, chRef, pbMiddlewaresService) {
|
|
1887
|
+
this.userInterfaceService = userInterfaceService;
|
|
1888
|
+
this.emailObjectStore = emailObjectStore;
|
|
1889
|
+
this.chRef = chRef;
|
|
1890
|
+
this.pbMiddlewaresService = pbMiddlewaresService;
|
|
1891
|
+
this.structure = new Structure();
|
|
1892
|
+
this.cdkDropListConnectedTo$ = this.userInterfaceService.cdkDropListConnectedTo$;
|
|
1893
|
+
this.validate = function ({ rectangle }) {
|
|
1894
|
+
const colFr = +(rectangle.width / this.fr).toFixed(2);
|
|
1895
|
+
return colFr > 1 && colFr <= 10 - this.structure.columns;
|
|
1896
|
+
}.bind(this);
|
|
1897
|
+
// sectionWidth$ = this.userInterfaceService.currentEditingStructure$.pipe(
|
|
1898
|
+
// withLatestFrom(this.emailObjectStore.emailBodyWidth$),
|
|
1899
|
+
// map(([{ options }, width]) => {
|
|
1900
|
+
// return options.fullWidth ? '100%' : width;
|
|
1901
|
+
// })
|
|
1902
|
+
// );
|
|
1903
|
+
this.componentIsDestroyed$ = new Subject();
|
|
1904
|
+
}
|
|
1905
|
+
get isStructureActive() {
|
|
1906
|
+
return this.isActive;
|
|
1907
|
+
}
|
|
1908
|
+
// @HostBinding('style.width') get sectionWidth(): string | number {
|
|
1909
|
+
// console.log(this.emailObjectStore.Email.general.width.value);
|
|
1910
|
+
// return this.structure.options.fullWidth ? '100%' : this.emailObjectStore.Email.general.width.value;
|
|
1911
|
+
// }
|
|
1912
|
+
// Don't close right panel on structure click
|
|
1913
|
+
onHostClick(event) {
|
|
1914
|
+
event.stopImmediatePropagation();
|
|
1915
|
+
this.editStructure();
|
|
1916
|
+
}
|
|
1917
|
+
removeStructure(event) {
|
|
1918
|
+
event.stopPropagation();
|
|
1919
|
+
this.pbMiddlewaresService
|
|
1920
|
+
.removeStructure(this.index, this.structure)
|
|
1921
|
+
.pipe(exhaustMap(() => this.userInterfaceService.confirmDialog$()), filter(removeAllowed => removeAllowed), take(1))
|
|
1922
|
+
.subscribe(() => {
|
|
1923
|
+
this.emailObjectStore.removeStructure(this.index);
|
|
1924
|
+
this.userInterfaceService.resetElements();
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
duplicateStructure(event) {
|
|
1928
|
+
event.stopPropagation();
|
|
1929
|
+
this.pbMiddlewaresService
|
|
1930
|
+
.duplicateStructure(this.index, this.structure)
|
|
1931
|
+
.pipe(map(() => this.emailObjectStore.duplicateStructure(this.index)), take(1))
|
|
1932
|
+
.subscribe(newStructure => {
|
|
1933
|
+
this.editStructure(newStructure);
|
|
1934
|
+
});
|
|
1935
|
+
}
|
|
1936
|
+
dropNewBlock(event, column) {
|
|
1937
|
+
if (event.previousContainer === event.container) {
|
|
1938
|
+
this.emailObjectStore.addBlock(event, column);
|
|
1939
|
+
}
|
|
1940
|
+
else {
|
|
1941
|
+
this.pbMiddlewaresService
|
|
1942
|
+
.addBlock(event, column)
|
|
1943
|
+
.pipe(map(newData => this.emailObjectStore.addBlock(newData.event, newData.column)))
|
|
1944
|
+
.subscribe(() => {
|
|
1945
|
+
// this.userInterfaceService.editBlock(newBlock);
|
|
1946
|
+
this.chRef.markForCheck();
|
|
1947
|
+
});
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
disableBlockDrag$(block) {
|
|
1951
|
+
return this.pbMiddlewaresService.disableBlockDragWithinEmailBody(block).pipe(take(1));
|
|
1952
|
+
}
|
|
1953
|
+
editStructure(structure = this.structure) {
|
|
1954
|
+
this.pbMiddlewaresService
|
|
1955
|
+
.editStructure(structure)
|
|
1956
|
+
.pipe(take(1))
|
|
1957
|
+
.subscribe(strctr => {
|
|
1958
|
+
this.userInterfaceService.editStructure(strctr);
|
|
1959
|
+
});
|
|
1960
|
+
}
|
|
1961
|
+
getResizeEdges(columnKey) {
|
|
1962
|
+
return {
|
|
1963
|
+
right: columnKey + 1 !== this.structure.columns,
|
|
1964
|
+
left: columnKey !== 0
|
|
1965
|
+
};
|
|
1966
|
+
}
|
|
1967
|
+
createColumnId(columnKey) {
|
|
1968
|
+
return `column-droplist-${columnKey}-${this.index}`;
|
|
1969
|
+
}
|
|
1970
|
+
getColumnStyles(columnKey) {
|
|
1971
|
+
const { options: { gaps = [4, 4], columns = [] } } = this.structure;
|
|
1972
|
+
const column = columns[columnKey] || defaultColumnsOptions;
|
|
1973
|
+
let verticalAlign = 'center';
|
|
1974
|
+
if (column.verticalAlign === 'bottom') {
|
|
1975
|
+
verticalAlign = 'flex-end';
|
|
1976
|
+
}
|
|
1977
|
+
else if (column.verticalAlign === 'top') {
|
|
1978
|
+
verticalAlign = 'flex-start';
|
|
1979
|
+
}
|
|
1980
|
+
return Object.assign({ padding: gaps.map(gap => `${gap}px`).join(' '), backgroundColor: column.background.color, placeSelf: `${verticalAlign} stretch` }, createBorder(column.border)
|
|
1981
|
+
// ...createBackground(column.background)
|
|
1982
|
+
);
|
|
1983
|
+
}
|
|
1984
|
+
onResizeEnd({ rectangle }, key) {
|
|
1985
|
+
this.structure.options.columnsWidth[key] = +(rectangle.width / this.fr).toFixed(2);
|
|
1986
|
+
}
|
|
1987
|
+
ngDoCheck() {
|
|
1988
|
+
if (this.isActive || this.userInterfaceService.currentStructureContainsActiveBlock(this.structure)) {
|
|
1989
|
+
this.chRef.markForCheck();
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
ngOnInit() {
|
|
1993
|
+
// Add columns options in case of old template
|
|
1994
|
+
const { columns, columnsWidth } = this.structure.options;
|
|
1995
|
+
if (!columns || !columnsWidth) {
|
|
1996
|
+
const { type, elements, options } = this.structure;
|
|
1997
|
+
this.editStructure(new Structure(type, elements, options));
|
|
1998
|
+
}
|
|
1999
|
+
const width = this.emailObjectStore.Email.general.width.value;
|
|
2000
|
+
const [, horizontalGap] = this.structure.options.gaps;
|
|
2001
|
+
this.fr = (width - horizontalGap * this.structure.columns) / 10;
|
|
2002
|
+
this.userInterfaceService.currentEditingStructure$
|
|
2003
|
+
.pipe(map(currentEditingStructure => currentEditingStructure === this.structure), takeUntil(this.componentIsDestroyed$))
|
|
2004
|
+
.subscribe(isActive => {
|
|
2005
|
+
this.isActive = isActive;
|
|
2006
|
+
});
|
|
2007
|
+
}
|
|
2008
|
+
ngOnDestroy() {
|
|
2009
|
+
this.componentIsDestroyed$.next('');
|
|
2010
|
+
this.componentIsDestroyed$.complete();
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
StructureComponent.decorators = [
|
|
2014
|
+
{ type: Component, args: [{
|
|
2015
|
+
selector: 'pb-structure',
|
|
2016
|
+
template: "<!--\n ~ Copyright (c) 2024 Pobuca.\n ~ All rights reserved.\n -->\n\n<div class=\"column\" *ngFor=\"let column of structure.elements; let index = index\" cdkDropList mwlResizable\n [enableGhostResize]=\"false\" [resizeSnapGrid]=\"{ left: fr, right: fr }\" [resizeEdges]=\"getResizeEdges(index)\"\n [validateResize]=\"validate\" (resizing)=\"onResizeEnd($event, index)\"\n (cdkDropListDropped)=\"dropNewBlock($event, column)\" [class.empty]=\"!column.length\" [ngStyle]=\"getColumnStyles(index)\"\n [cdkDropListConnectedTo]=\"cdkDropListConnectedTo$ | async\" [cdkDropListData]=\"column\" [id]=\"createColumnId(index)\">\n\n <div *ngIf=\"!column.length\" class=\"empty-block\" i18n=\"structure|Inside empty block\">DnD blocks</div>\n\n <pb-block *ngFor=\"let block of column; let index = index\" cdkDrag [cdkDragData]=\"block\" [block]=\"block\"\n [index]=\"index\" [column]=\"column\" [cdkDragDisabled]=\"disableBlockDrag$(block) | async\">\n <button mat-icon-button class=\"move\" cdkDragHandle i18n-matTooltip=\"block|Change Block order\"\n matTooltip=\"Change Block order\" *ngIf=\"!(disableBlockDrag$(block) | async)\">\n <mat-icon i18n-aria-label=\"block|Move block\" aria-label=\"Move block\" inline>pan_tool</mat-icon>\n </button>\n </pb-block>\n</div>\n\n<ng-content select=\".move\"></ng-content>\n<div class=\"tools\" fxLayoutGap=\"0.25rem\">\n <button mat-icon-button class=\"edit\" color=\"primary\" i18n-matTooltip=\"structure|Change structure settings\"\n matTooltip=\"Change structure settings\">\n <mat-icon i18n-aria-label=\"structure|Edit structure\" aria-label=\"Edit structure\" inline>edit</mat-icon>\n </button>\n <button mat-icon-button (click)=\"duplicateStructure($event)\" i18n-matTooltip=\"structure|Duplicate structure\"\n matTooltip=\"Duplicate structure\">\n <mat-icon i18n-aria-label=\"structure|Duplicate structure\" aria-label=\"Duplicate structure\" inline>file_copy\n </mat-icon>\n </button>\n <button mat-icon-button color=\"warn\" (click)=\"removeStructure($event)\" i18n-matTooltip=\"structure|Delete structure\"\n matTooltip=\"Delete structure\">\n <mat-icon i18n-aria-label=\"structure|Remove Structure\" aria-label=\"Remove Structure\" inline>delete_forever\n </mat-icon>\n </button>\n</div>\n",
|
|
2017
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2018
|
+
encapsulation: ViewEncapsulation.Emulated,
|
|
2019
|
+
exportAs: 'structure',
|
|
2020
|
+
styles: ["/*!\n * Copyright (c) 2024 Pobuca.\n * All rights reserved.\n */pb-block>.cdk-drag-handle{position:absolute;top:-30px;right:0;cursor:move;background-color:rgba(63,81,181,.5);color:#fff;border-radius:50% 50% 0 0;opacity:0;height:30px;width:30px;font-size:smaller}pb-block>.cdk-drag-handle mat-icon{margin-top:-8px}pb-block>.cdk-drag-handle:hover{background-color:#3f51b5}pb-block.cdk-drag-preview .cdk-drag-handle,pb-block:hover .cdk-drag-handle{opacity:1}:host{display:grid;border:1px solid #ccc;position:relative;grid-template-columns:1fr}:host>.tools{direction:ltr;position:absolute;opacity:0;left:0;bottom:-25px;color:#ff4081}:host>.tools button{background-color:#fff;border-radius:0 0 3px 3px;height:25px;width:25px;line-height:0}:host.active,:host.cdk-drag-placeholder,:host.cdk-drag-preview,:host:hover{box-shadow:0 0 1px 2px #ff4081;z-index:1}:host:hover>.tools{opacity:1}:host:hover>.tools button{background-color:#ff4081;color:#fff}:host.active>.tools>.edit{display:none}:host.cols_2{grid-template-columns:repeat(2,6fr)}:host.cols_3{grid-template-columns:repeat(3,4fr)}:host.cols_4{grid-template-columns:repeat(4,3fr)}:host.cols_12{grid-template-columns:7fr 5fr}:host.cols_21{grid-template-columns:5fr 7fr}:host .cdk-drop-list-dragging,:host .cdk-drop-list-receiving{height:100%}:host>.column{box-sizing:border-box}:host>.column.empty{height:60px}:host>.column.empty .empty-block{background-color:rgba(101,99,99,.21);height:100%;display:flex;justify-content:center;place-items:center;flex-direction:column;color:rgba(101,99,99,.3);font-size:small}:host>.column.empty.cdk-drop-list-dragging .empty-block{display:none}:host>.column.empty>.cdk-drag-placeholder{min-height:60px!important}"]
|
|
2021
|
+
},] }
|
|
2022
|
+
];
|
|
2023
|
+
StructureComponent.ctorParameters = () => [
|
|
2024
|
+
{ type: PbUserInterfaceService },
|
|
2025
|
+
{ type: PbEmailObjectStoreService },
|
|
2026
|
+
{ type: ChangeDetectorRef },
|
|
2027
|
+
{ type: PbUserMiddlewaresService }
|
|
2028
|
+
];
|
|
2029
|
+
StructureComponent.propDecorators = {
|
|
2030
|
+
structure: [{ type: Input }],
|
|
2031
|
+
index: [{ type: Input }],
|
|
2032
|
+
isStructureActive: [{ type: HostBinding, args: ['class.active',] }],
|
|
2033
|
+
onHostClick: [{ type: HostListener, args: ['click', ['$event'],] }]
|
|
2034
|
+
};
|
|
2035
|
+
|
|
2036
|
+
/*
|
|
2037
|
+
* Copyright (c) 2024 Pobuca.
|
|
2038
|
+
* All rights reserved.
|
|
2039
|
+
*/
|
|
2040
|
+
/**
|
|
2041
|
+
* @ignore
|
|
2042
|
+
*
|
|
2043
|
+
* Soon will be removed.
|
|
2044
|
+
*/
|
|
2045
|
+
class SlugifyPipe {
|
|
2046
|
+
transform(input) {
|
|
2047
|
+
const trChars = {
|
|
2048
|
+
áÁ: 'a',
|
|
2049
|
+
éÉ: 'e',
|
|
2050
|
+
íÍ: 'i',
|
|
2051
|
+
óÓ: 'o',
|
|
2052
|
+
úÚ: 'u',
|
|
2053
|
+
ñÑ: 'n'
|
|
2054
|
+
};
|
|
2055
|
+
for (const key of Object.keys(trChars)) {
|
|
2056
|
+
input = input.replace(new RegExp('[' + key + ']', 'g'), trChars[key]);
|
|
2057
|
+
}
|
|
2058
|
+
return input
|
|
2059
|
+
.toString()
|
|
2060
|
+
.toLowerCase()
|
|
2061
|
+
.replace(/\s+/g, '-') // Replace spaces with -
|
|
2062
|
+
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
|
|
2063
|
+
.replace(/\-\-+/g, '-') // Replace multiple - with single -
|
|
2064
|
+
.replace(/^-+/, '') // Trim - from start of text
|
|
2065
|
+
.replace(/-+$/, ''); // Trim - from end of text
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
SlugifyPipe.decorators = [
|
|
2069
|
+
{ type: Pipe, args: [{
|
|
2070
|
+
name: 'slugify'
|
|
2071
|
+
},] }
|
|
2072
|
+
];
|
|
2073
|
+
|
|
2074
|
+
/*
|
|
2075
|
+
* Copyright (c) 2024 Pobuca.
|
|
2076
|
+
* All rights reserved.
|
|
2077
|
+
*/
|
|
2078
|
+
class PbEmailBuilderComponent {
|
|
2079
|
+
constructor(ngb, userRestApiService, userInterfaceService, emailObjectStore, renderer2, slugifyPipe, middlewares, changeDetectorRef, structures, blocks, googleFonts, doc) {
|
|
2080
|
+
this.ngb = ngb;
|
|
2081
|
+
this.userRestApiService = userRestApiService;
|
|
2082
|
+
this.userInterfaceService = userInterfaceService;
|
|
2083
|
+
this.emailObjectStore = emailObjectStore;
|
|
2084
|
+
this.renderer2 = renderer2;
|
|
2085
|
+
this.slugifyPipe = slugifyPipe;
|
|
2086
|
+
this.middlewares = middlewares;
|
|
2087
|
+
this.changeDetectorRef = changeDetectorRef;
|
|
2088
|
+
this.structures = structures;
|
|
2089
|
+
this.blocks = blocks;
|
|
2090
|
+
this.doc = doc;
|
|
2091
|
+
this.emailChange = new EventEmitter();
|
|
2092
|
+
this.previewTemplate = false;
|
|
2093
|
+
this.showGeneralSettings$ = this.userInterfaceService.editGeneralSettings$;
|
|
2094
|
+
this.currentTabIndex$ = this.userInterfaceService.currentTabIndex$;
|
|
2095
|
+
this.cdkDropListConnectedTo$ = this.userInterfaceService.cdkDropListConnectedTo$;
|
|
2096
|
+
this.getBuilderContainerStyles$ = this.emailObjectStore.builderContainerStyles$;
|
|
2097
|
+
this.currentHTMLTemplate$ = this.emailObjectStore.templateAsObservable$;
|
|
2098
|
+
this.activeMatProgress$ = this.userInterfaceService.activeMatProgress$.asObservable();
|
|
2099
|
+
this.customModuleList$ = this.userRestApiService.getAllUserModules$;
|
|
2100
|
+
this._onDestroy$ = new Subject();
|
|
2101
|
+
this.includedFonts = new Set();
|
|
2102
|
+
if (this.doc) {
|
|
2103
|
+
for (const font of googleFonts) {
|
|
2104
|
+
const link = this.doc.createElement('link');
|
|
2105
|
+
link.href = `https://fonts.googleapis.com/css?family=${font}`;
|
|
2106
|
+
link.rel = 'stylesheet';
|
|
2107
|
+
this.includedFonts.add(link);
|
|
2108
|
+
this.doc.head.appendChild(link);
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
writeValue(email) {
|
|
2113
|
+
try {
|
|
2114
|
+
this.emailObjectStore.setEmail(email);
|
|
2115
|
+
this.previewTemplate = false;
|
|
2116
|
+
this.changeDetectorRef.markForCheck();
|
|
2117
|
+
}
|
|
2118
|
+
catch (error) {
|
|
2119
|
+
this.userInterfaceService.notify(error.message);
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
registerOnChange(fn) {
|
|
2123
|
+
this.onChange = fn;
|
|
2124
|
+
}
|
|
2125
|
+
registerOnTouched(fn) {
|
|
2126
|
+
this.onTouched = fn;
|
|
2127
|
+
}
|
|
2128
|
+
/**
|
|
2129
|
+
* Not implemented yet
|
|
2130
|
+
* @param isDisabled The state of builder
|
|
2131
|
+
*/
|
|
2132
|
+
setDisabledState(isDisabled) {
|
|
2133
|
+
// tslint:disable-next-line: no-console
|
|
2134
|
+
console.info('[IpEmailBuilderComponent] Method [setDisabledState] is not implemented.');
|
|
2135
|
+
}
|
|
2136
|
+
saveEmail() {
|
|
2137
|
+
return this.emailObjectStore.currentEmailHasChanges$
|
|
2138
|
+
.pipe(exhaustMap(hasChanges => {
|
|
2139
|
+
return iif(() => hasChanges, this.ngb.createHTMLTemplate$(), this.userInterfaceService.notify('No changes were detected to be save!').afterDismissed());
|
|
2140
|
+
}), take(1))
|
|
2141
|
+
.subscribe();
|
|
2142
|
+
}
|
|
2143
|
+
disableBlocksList$(block) {
|
|
2144
|
+
return iif(() => { var _a; return !((_a = block.state) === null || _a === void 0 ? void 0 : _a.disabled); }, this.emailObjectStore.emailStructuresAsObservable$.pipe(map(({ length }) => length > 0), exhaustMap(hasStructures => (hasStructures ? this.middlewares.disableBlockDragFromList(block) : deferOf(true))))).pipe(take(1));
|
|
2145
|
+
}
|
|
2146
|
+
disableStructureList$(structure) {
|
|
2147
|
+
return this.middlewares.disableStructureDragFromList(structure).pipe(take(1));
|
|
2148
|
+
}
|
|
2149
|
+
trackByFn(block) {
|
|
2150
|
+
return block.type;
|
|
2151
|
+
}
|
|
2152
|
+
changeTabIndex(index) {
|
|
2153
|
+
this.userInterfaceService.changeTabIndex(index);
|
|
2154
|
+
}
|
|
2155
|
+
download(source) {
|
|
2156
|
+
return this.middlewares
|
|
2157
|
+
.exportFile(source)
|
|
2158
|
+
.pipe(exhaustMap(() => this.ngb.createHTMLTemplate$().pipe(map(serverResponse => (Object.assign(Object.assign({}, serverResponse), { type: source }))))), catchError(error => this.middlewares.catchError(error).pipe(switchMap(() => {
|
|
2159
|
+
this.userInterfaceService.notify(error.message);
|
|
2160
|
+
return EMPTY;
|
|
2161
|
+
}))), take(1))
|
|
2162
|
+
.subscribe(({ type, html, mjml }) => {
|
|
2163
|
+
const anchor = this.renderer2.createElement('a');
|
|
2164
|
+
let download = html;
|
|
2165
|
+
if (type === 'mjml') {
|
|
2166
|
+
download = mjml;
|
|
2167
|
+
}
|
|
2168
|
+
else if (type === 'json') {
|
|
2169
|
+
download = JSON.stringify(this.ngb.Email, null, 1);
|
|
2170
|
+
}
|
|
2171
|
+
const href = URL.createObjectURL(new Blob([download], {
|
|
2172
|
+
type: type === 'json' ? 'text/json' : 'text/html'
|
|
2173
|
+
}));
|
|
2174
|
+
const fileName = this.slugifyPipe.transform(this.ngb.Email.general.name || 'ngb-template');
|
|
2175
|
+
anchor.href = href;
|
|
2176
|
+
anchor.target = '_blank';
|
|
2177
|
+
anchor.download = `${fileName}.${type}`;
|
|
2178
|
+
anchor.click();
|
|
2179
|
+
URL.revokeObjectURL(href);
|
|
2180
|
+
});
|
|
2181
|
+
}
|
|
2182
|
+
importFile() {
|
|
2183
|
+
this.userInterfaceService
|
|
2184
|
+
.importDialog$()
|
|
2185
|
+
.pipe(switchMap(emailOrError => {
|
|
2186
|
+
if (emailOrError instanceof Error) {
|
|
2187
|
+
throw emailOrError;
|
|
2188
|
+
}
|
|
2189
|
+
return deferOf(emailOrError);
|
|
2190
|
+
}), catchError(error => this.middlewares.catchError(error).pipe(switchMap(() => {
|
|
2191
|
+
this.userInterfaceService.notify(error.message);
|
|
2192
|
+
return EMPTY;
|
|
2193
|
+
}))), take(1), filter(email => !!email))
|
|
2194
|
+
.subscribe(email => {
|
|
2195
|
+
this.writeValue(email);
|
|
2196
|
+
this.userInterfaceService.notify('Email has been imported successfully.');
|
|
2197
|
+
});
|
|
2198
|
+
}
|
|
2199
|
+
// getBackgroundImage(): SafeStyle {
|
|
2200
|
+
// const {
|
|
2201
|
+
// background: { url }
|
|
2202
|
+
// } = this.email.general;
|
|
2203
|
+
// return this.sanitizer.bypassSecurityTrustStyle(url && `url(${url})`);
|
|
2204
|
+
// }
|
|
2205
|
+
// getBuilderContainerStyles() {
|
|
2206
|
+
// const { background, padding, direction } = this.emailObjectStore.Email.general;
|
|
2207
|
+
// return {
|
|
2208
|
+
// direction,
|
|
2209
|
+
// backgroundRepeat: background.repeat,
|
|
2210
|
+
// backgroundColor: background.color,
|
|
2211
|
+
// backgroundSize: createWidthHeight(background.size),
|
|
2212
|
+
// backgroundPosition: 'top center',
|
|
2213
|
+
// ...createPadding(padding)
|
|
2214
|
+
// };
|
|
2215
|
+
// }
|
|
2216
|
+
createArrayFromStructureColumns({ columns }) {
|
|
2217
|
+
return new Array(columns).fill('');
|
|
2218
|
+
}
|
|
2219
|
+
togglePreview() {
|
|
2220
|
+
return this.emailObjectStore
|
|
2221
|
+
.createHTMLTemplate$()
|
|
2222
|
+
.pipe(exhaustMap(() => this.middlewares.togglePreview(!this.previewTemplate)), catchError(error => this.middlewares.catchError(error).pipe(switchMap(() => {
|
|
2223
|
+
this.userInterfaceService.notify(error.message);
|
|
2224
|
+
return EMPTY;
|
|
2225
|
+
}))), take(1))
|
|
2226
|
+
.subscribe(nextState => {
|
|
2227
|
+
this.previewTemplate = nextState;
|
|
2228
|
+
this.changeDetectorRef.markForCheck();
|
|
2229
|
+
});
|
|
2230
|
+
}
|
|
2231
|
+
ngOnInit() {
|
|
2232
|
+
fromEvent(window, 'beforeunload')
|
|
2233
|
+
.pipe(withLatestFrom(this.emailObjectStore.currentEmailHasChanges$), filter(([_, emailIsDirty]) => emailIsDirty), switchMap(([event]) => this.middlewares.preventWindowExit(event)), takeUntil(this._onDestroy$))
|
|
2234
|
+
.subscribe(ev => {
|
|
2235
|
+
ev.preventDefault();
|
|
2236
|
+
ev.returnValue = '';
|
|
2237
|
+
});
|
|
2238
|
+
this.emailObjectStore.emailAsObservable$.pipe(takeUntil(this._onDestroy$)).subscribe(email => {
|
|
2239
|
+
var _a, _b;
|
|
2240
|
+
(_a = this.onTouched) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
2241
|
+
(_b = this.onChange) === null || _b === void 0 ? void 0 : _b.call(this, email);
|
|
2242
|
+
this.emailChange.next(email);
|
|
2243
|
+
});
|
|
2244
|
+
}
|
|
2245
|
+
ngOnChanges(changes) {
|
|
2246
|
+
if (changes.email) {
|
|
2247
|
+
this.writeValue(changes.email.currentValue);
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
// private setEmail(email: PBEmail) {
|
|
2251
|
+
// try {
|
|
2252
|
+
// this.emailObjectStore.setEmail(email);
|
|
2253
|
+
// this.previewTemplate = false;
|
|
2254
|
+
// this.changeDetectorRef.markForCheck();
|
|
2255
|
+
// } catch (error) {
|
|
2256
|
+
// this.userInterfaceService.notify(error.message);
|
|
2257
|
+
// }
|
|
2258
|
+
// }
|
|
2259
|
+
ngOnDestroy() {
|
|
2260
|
+
this._onDestroy$.next();
|
|
2261
|
+
this._onDestroy$.complete();
|
|
2262
|
+
// Reset the builder state
|
|
2263
|
+
this.userInterfaceService.reset();
|
|
2264
|
+
if (this.doc) {
|
|
2265
|
+
this.includedFonts.forEach(font => font.remove());
|
|
2266
|
+
}
|
|
2267
|
+
this.includedFonts.clear();
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
PbEmailBuilderComponent.decorators = [
|
|
2271
|
+
{ type: Component, args: [{
|
|
2272
|
+
selector: 'pb-email-builder',
|
|
2273
|
+
template: "<!--\n ~ Copyright (c) 2024 Pobuca.\n ~ All rights reserved.\n -->\n\n<mat-sidenav-container>\n <div class=\"pb-builder-container\" fxLayout=\"column\" fxFlex>\n <mat-progress-bar *ngIf=\"activeMatProgress$ | async\" mode=\"buffer\"></mat-progress-bar>\n <mat-toolbar fxLayoutGap=\"1rem\" fxLayoutAlign=\"space-between center\">\n <ng-content fxLayout fxLayoutGap=\"0.5rem\" select=\".top-actions\"></ng-content>\n\n <div fxFlexlayout fxLayoutGap=\"0.5rem\">\n <button *ngIf=\"ngb.config.useSaveButton\" type=\"button\" (click)=\"saveEmail()\" mat-stroked-button color=\"primary\"\n i18n=\"actions|Save Email\">\n Save Email\n </button>\n <button *ngIf=\"ngb.config.usePreviewButton\" type=\"button\" (click)=\"togglePreview()\" mat-stroked-button>\n <ng-container [ngSwitch]=\"previewTemplate\">\n <span *ngSwitchDefault i18n=\"actions|Preview\">Preview</span>\n <span *ngSwitchCase=\"true\" i18n=\"actions|Close Preview\">Close Preview</span>\n </ng-container>\n </button>\n\n <button mat-button mat-stroked-button i18n=\"actions|Import\" (click)=\"importFile()\">\n Import\n </button>\n\n <ng-container *ngIf=\"ngb.config.useDownloadButton\">\n <button mat-button [matMenuTriggerFor]=\"downloadMenu\" mat-stroked-button i18n=\"actions|Export\">\n Export\n </button>\n <mat-menu #downloadMenu=\"matMenu\">\n <button mat-menu-item (click)=\"download('html')\" i18n=\"actions|Export HTML\">\n Export HTML\n </button>\n <button mat-menu-item (click)=\"download('mjml')\" i18n=\"actions|Export MJML\">\n Export MJML\n </button>\n <button mat-menu-item (click)=\"download('json')\" i18n=\"actions|Export JSON\">\n Export JSON\n </button>\n </mat-menu>\n </ng-container>\n\n </div>\n <!-- <div class=\"history-actions\" style=\"margin-inline-start: auto;\">\n <span>Last saved Now</span>\n <button type=\"button\" mat-icon-button>\n <mat-icon>undo</mat-icon>\n </button>\n <button type=\"button\" mat-icon-button>\n <mat-icon>redo</mat-icon>\n </button>\n </div> -->\n </mat-toolbar>\n <ng-container [ngSwitch]=\"previewTemplate\">\n <pb-builder-container *ngSwitchDefault [ngStyle]=\"getBuilderContainerStyles$ | async\">\n <ng-content select=\".rewrited-empty-email\"></ng-content>\n </pb-builder-container>\n\n <pb-preview-template fxLayout fxFlex=\"auto\" fxLayoutAlign=\"center\" [template]=\"currentHTMLTemplate$ | async\"\n *ngSwitchCase=\"true\">\n </pb-preview-template>\n </ng-container>\n </div>\n <mat-sidenav mode=\"side\" fxFlex=\"0 0 310px\" [opened]=\"!previewTemplate\" position=\"end\" class=\"pb-builder-content\">\n <mat-tab-group [selectedIndex]=\"currentTabIndex$ | async\" (selectedIndexChange)=\"changeTabIndex($event)\">\n <mat-tab i18n-label=\"Content\" label=\"Content\">\n <div class=\"main-padding\">\n <cdk-drop-list class=\"elements\" [cdkDropListConnectedTo]=\"cdkDropListConnectedTo$ | async\"\n [cdkDropListData]=\"blocks\" [cdkDropListSortingDisabled]=\"true\" id=\"block-elements\">\n <div cdkDrag *ngFor=\"let block of blocks; trackBy: trackByFn\" [cdkDragData]=\"block\"\n [cdkDragDisabled]=\"disableBlocksList$(block) | async\" class=\"drag-element\"\n [matTooltip]=\"block.state.message\">\n <mat-icon>{{ block.icon }}</mat-icon>\n <div class=\"drag-element-title\">{{ block.title }}</div>\n </div>\n </cdk-drop-list>\n\n <mat-divider style=\"margin: 1em 0;\"></mat-divider>\n <!-- <h3 class=\"divider\"><span>Structures</span></h3> -->\n <cdk-drop-list class=\"elements structure-elements\" cdkDropListConnectedTo=\"structures-drop-list\"\n [cdkDropListSortingDisabled]=\"true\" [cdkDropListData]=\"structures\">\n <div cdkDrag *ngFor=\"let structure of structures; trackBy: trackByFn\" [cdkDragData]=\"structure\"\n [cdkDragDisabled]=\"disableStructureList$(structure) | async\" class=\"drag-element structure-element\"\n [ngClass]=\"structure.type\">\n <div *ngFor=\"let column of createArrayFromStructureColumns(structure)\"></div>\n </div>\n </cdk-drop-list>\n <ng-content fxLayout fxLayoutGap=\"0.5rem\" select=\".after-structure-blocks\"></ng-content>\n </div>\n </mat-tab>\n <mat-tab i18n-label=\"Modules\" label=\"Modules\" class=\"modules-tab\">\n <cdk-virtual-scroll-viewport itemSize=\"54\" [minBufferPx]=\"54 * 10\" [maxBufferPx]=\"54 * 20\"\n style=\"height: inherit;\"\n *ngIf=\"customModuleList$ | async as modules; else emptyModuleList\">\n <div class=\"main-padding\">\n <div class=\"custom-modules\" cdkDropList cdkDropListConnectedTo=\"structures-drop-list\"\n [cdkDropListData]=\"modules\" [cdkDropListSortingDisabled]=\"true\">\n <div fxLayout=\"column\" fxLayoutGap=\"1rem\">\n <ng-container *cdkVirtualFor=\"let module of modules\">\n <div cdkDrag [cdkDragData]=\"module.module\"\n [cdkDragDisabled]=\"disableStructureList$(module.module) | async\" class=\"drag-element\"\n style=\"padding: 1em;text-transform: uppercase;height: 54px;\">\n <span *ngIf=\"module.name\">{{module.name}}</span>\n <!-- <img *ngIf=\"module.thumb\" [src]=\"module.thumb\" /> -->\n </div>\n </ng-container>\n </div>\n </div>\n <ng-content fxLayout fxLayoutGap=\"0.5rem\" select=\".after-content-blocks\"></ng-content>\n </div>\n </cdk-virtual-scroll-viewport>\n <ng-template #emptyModuleList>\n <h4 class=\"main-padding\" style=\"text-align: center;\" i18n=\"modules|empty list message\">\n Module list is empty\n </h4>\n </ng-template>\n </mat-tab>\n <mat-tab i18n-label=\"Settings\" label=\"Settings\">\n <div class=\"pb-builder-options\" [ngSwitch]=\"showGeneralSettings$ | async\">\n <pb-general-settings *ngSwitchDefault></pb-general-settings>\n <pb-block-settings *ngSwitchCase=\"'block'\"></pb-block-settings>\n <pb-structure-settings *ngSwitchCase=\"'structure'\" fxLayout=\"column\"></pb-structure-settings>\n </div>\n </mat-tab>\n </mat-tab-group>\n </mat-sidenav>\n</mat-sidenav-container>\n",
|
|
2274
|
+
exportAs: 'pbEmailBuilder',
|
|
2275
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2276
|
+
providers: [
|
|
2277
|
+
{
|
|
2278
|
+
provide: NG_VALUE_ACCESSOR,
|
|
2279
|
+
useExisting: forwardRef(() => PbEmailBuilderComponent),
|
|
2280
|
+
multi: true
|
|
2281
|
+
},
|
|
2282
|
+
SlugifyPipe
|
|
2283
|
+
],
|
|
2284
|
+
styles: ["/*!\n * Copyright (c) 2024 Pobuca.\n * All rights reserved.\n */:host{display:block;height:100vh}.hide{display:none}::ng-deep mat-sidenav-container{height:100%}::ng-deep mat-sidenav-container mat-sidenav-content{width:100%;z-index:unset!important;overflow:unset!important}::ng-deep h3.divider{text-align:center;display:block;overflow:hidden;margin:0 0 1rem}::ng-deep h3.divider span{font-weight:100;position:relative;display:inline-block}::ng-deep h3.divider span:after,::ng-deep h3.divider span:before{content:\"\";position:absolute;top:50%;height:1px;background:#ccc;width:99999px}::ng-deep h3.divider span:before{left:100%;margin-left:10px}::ng-deep h3.divider span:after{right:100%;margin-right:10px}::ng-deep .pb-builder-content{border-right:1px solid rgba(0,0,0,.12)}::ng-deep .pb-builder-content .overflow{position:absolute;top:0;bottom:0;left:0;right:0;background-color:#fff;opacity:.5;display:none;z-index:3}::ng-deep .pb-builder-content.disabled .overflow{display:block}::ng-deep .pb-builder-content .mat-tab-label{width:50%;min-width:auto;height:64px;font-size:medium;text-transform:uppercase;padding:0 12px}::ng-deep .pb-builder-content .mat-tab-body-wrapper,::ng-deep .pb-builder-content .mat-tab-group{height:100%}::ng-deep .pb-builder-content .mat-tab-body-wrapper .mat-tab-body{padding:0}::ng-deep .pb-builder-content .mat-tab-body-wrapper .mat-tab-body .elements{display:grid;grid-gap:.5rem;margin-bottom:1rem;grid-template:repeat(2,1fr)/repeat(3,1fr)}::ng-deep .pb-builder-content .mat-tab-body-wrapper .mat-tab-body .elements.structure-elements{grid-template:repeat(4,1fr)/1fr}::ng-deep .pb-builder-content .mat-tab-body-wrapper .mat-tab-body .elements.cdk-drop-list-dragging .cdk-drag{transform:none!important}::ng-deep .pb-builder-content .mat-tab-body-wrapper .mat-tab-body input{height:auto;border:none}::ng-deep .pb-builder-content .mat-tab-body-wrapper .mat-tab-body .custom-modules .drag-element img{max-width:100%}::ng-deep .pb-builder-container{position:relative;height:100%}::ng-deep .pb-builder-container mat-progress-bar{position:absolute}::ng-deep .pb-builder-container mat-toolbar{background-color:#fff}::ng-deep .pb-builder-options .mat-expansion-panel{box-shadow:none!important}::ng-deep .pb-builder-options .mat-expansion-panel.mat-expanded{overflow:visible}::ng-deep .pb-builder-options mat-expansion-panel-header:not(.mat-expanded){background:rgba(0,0,0,.04)}::ng-deep .pb-builder-options ::ng-deep .group{display:grid;grid-template:auto/repeat(2,1fr);grid-gap:1rem}::ng-deep .pb-builder-options ::ng-deep .group>*{align-self:center}::ng-deep .pb-builder-options ::ng-deep .group+.group{margin-top:1em}::ng-deep .pb-builder-options ::ng-deep .group.four{grid-template-columns:repeat(4,1fr)}::ng-deep .pb-builder-options ::ng-deep .group.f-large{grid-template-columns:2fr 1fr}::ng-deep .pb-builder-options ::ng-deep .group.l-large{grid-template-columns:1fr 2fr}::ng-deep .pb-builder-options ::ng-deep .group.three{grid-template-columns:repeat(3,1fr)}::ng-deep .pb-builder-options ::ng-deep .group.three.f-small{grid-template-columns:1fr repeat(2,2fr)}::ng-deep .pb-builder-options ::ng-deep .group.three.f-large{grid-template-columns:2fr repeat(2,1fr)}::ng-deep .pb-builder-options ::ng-deep .group.three.l-large{grid-template-columns:repeat(2,1fr) 2fr}::ng-deep .pb-builder-options ::ng-deep .group .mat-form-field-infix{width:auto!important}::ng-deep .drag-element{text-align:center;border:1px solid #ccc;box-sizing:border-box;background:#fff;color:#7b7b7b;cursor:move;transition:box-shadow .3s ease-in-out;will-change:box-shadow;border-radius:3px}::ng-deep .drag-element.cdk-drag-disabled,::ng-deep .drag-element.cdk-drag-placeholder{cursor:not-allowed;opacity:.6}::ng-deep .drag-element:hover{box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12)}::ng-deep .drag-element .mat-icon{font-size:40px!important;padding:.5rem;width:auto;height:auto}::ng-deep .drag-element img{max-width:100%}::ng-deep .drag-element-title{font-size:.9em;text-transform:uppercase;margin-bottom:.5rem}::ng-deep .drag-element.structure-element{height:60px;position:relative;padding:4px;display:grid;grid-template:1fr/1fr}::ng-deep .drag-element.structure-element>div{background:#ccc;border:4px solid #bbb}::ng-deep .drag-element.structure-element.cols_2{grid-template-columns:repeat(2,5fr)}::ng-deep .drag-element.structure-element.cols_3{grid-template-columns:repeat(3,3.33fr)}::ng-deep .drag-element.structure-element.cols_4{grid-template-columns:repeat(4,2.5fr)}::ng-deep .drag-element.structure-element.cols_12{grid-template-columns:6fr 4fr}::ng-deep .drag-element.structure-element.cols_21{grid-template-columns:4fr 6fr}::ng-deep .main-padding{padding:1rem}::ng-deep .cdk-drop-list-receiving>:not(.cdk-drag){opacity:.6}::ng-deep .cdk-drop-list-dragging>*{pointer-events:none}@keyframes drag-animation{0%{opacity:0;height:0}to{opacity:1}}::ng-deep .cdk-drag-animating{animation:drag-animation 1s cubic-bezier(0,0,.2,1) infinite}::ng-deep .cdk-drag-preview{border-radius:4px;box-shadow:0 0 0 2px rgba(63,81,181,.5);min-height:60px;min-width:100px;background-color:#fff}::ng-deep .cdk-drag.structure-element.cdk-drag-placeholder{box-shadow:0 0 1px 2px #ff4081;width:100%;pointer-events:none}"]
|
|
2285
|
+
},] }
|
|
2286
|
+
];
|
|
2287
|
+
PbEmailBuilderComponent.ctorParameters = () => [
|
|
2288
|
+
{ type: PbEmailBuilderService },
|
|
2289
|
+
{ type: PbUserRestApiService },
|
|
2290
|
+
{ type: PbUserInterfaceService },
|
|
2291
|
+
{ type: PbEmailObjectStoreService },
|
|
2292
|
+
{ type: Renderer2 },
|
|
2293
|
+
{ type: SlugifyPipe },
|
|
2294
|
+
{ type: PbUserMiddlewaresService },
|
|
2295
|
+
{ type: ChangeDetectorRef },
|
|
2296
|
+
{ type: Array, decorators: [{ type: Inject, args: [PB_STRUCTURES,] }] },
|
|
2297
|
+
{ type: Array, decorators: [{ type: Inject, args: [PB_BLOCKS,] }] },
|
|
2298
|
+
{ type: Array, decorators: [{ type: Inject, args: [PB_GOOGLE_FONTS,] }] },
|
|
2299
|
+
{ type: Document, decorators: [{ type: Inject, args: [DOCUMENT,] }] }
|
|
2300
|
+
];
|
|
2301
|
+
PbEmailBuilderComponent.propDecorators = {
|
|
2302
|
+
email: [{ type: Input }],
|
|
2303
|
+
emailChange: [{ type: Output }]
|
|
2304
|
+
};
|
|
2305
|
+
|
|
2306
|
+
/*
|
|
2307
|
+
* Copyright (c) 2024 Pobuca.
|
|
2308
|
+
* All rights reserved.
|
|
2309
|
+
*/
|
|
2310
|
+
// tslint:disable-next-line:no-shadowed-variable directive-class-suffix
|
|
2311
|
+
class AbstractBlock {
|
|
2312
|
+
ngOnInit() {
|
|
2313
|
+
// if (!this.portal) {
|
|
2314
|
+
// throw new Error(`Each element should have an options' portal, add *cdkPortal inside of HTML block.`);
|
|
2315
|
+
// }
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
AbstractBlock.decorators = [
|
|
2319
|
+
{ type: Directive }
|
|
2320
|
+
];
|
|
2321
|
+
AbstractBlock.propDecorators = {
|
|
2322
|
+
portal: [{ type: ViewChild, args: [CdkPortal, { static: true },] }]
|
|
2323
|
+
};
|
|
2324
|
+
class AbstractEntityBlock {
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
/*
|
|
2328
|
+
* Copyright (c) 2024 Pobuca.
|
|
2329
|
+
* All rights reserved.
|
|
2330
|
+
*/
|
|
2331
|
+
class TextElementComponent extends AbstractBlock {
|
|
2332
|
+
constructor(userRestApi, emailObject, internalService) {
|
|
2333
|
+
super();
|
|
2334
|
+
this.userRestApi = userRestApi;
|
|
2335
|
+
this.emailObject = emailObject;
|
|
2336
|
+
this.internalService = internalService;
|
|
2337
|
+
// @Input() block: TextBlock;
|
|
2338
|
+
this.setAsReadOnly$ = this.internalService.currentEditingBlock$.pipe(map(editingBlock => editingBlock !== this.block));
|
|
2339
|
+
this.staticQuillConfig = {
|
|
2340
|
+
toolbar: {
|
|
2341
|
+
container: [
|
|
2342
|
+
['bold', 'italic', 'underline', 'strike'],
|
|
2343
|
+
[{ header: [1, 2, 3, 4, 5, 6, false] }, { size: ['small', false, 'large', 'huge'] }],
|
|
2344
|
+
[{ align: [] }, 'link'],
|
|
2345
|
+
[{ list: 'ordered' }, { list: 'bullet' }],
|
|
2346
|
+
[{ color: [] }, { background: [] }],
|
|
2347
|
+
['clean']
|
|
2348
|
+
],
|
|
2349
|
+
handlers: {
|
|
2350
|
+
placeholder(selector) {
|
|
2351
|
+
const range = this.quill.getSelection();
|
|
2352
|
+
const format = this.quill.getFormat();
|
|
2353
|
+
const text = this.quill.getText(range.index, range.length);
|
|
2354
|
+
this.quill.deleteText(range.index, text.length);
|
|
2355
|
+
this.quill.insertText(range.index, selector, format);
|
|
2356
|
+
this.quill.setSelection(range.index, selector.length);
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
};
|
|
2361
|
+
this.quillInit$ = this.userRestApi.getAllUserMergeFields$.pipe(map(placeholder => {
|
|
2362
|
+
// @ts-ignore
|
|
2363
|
+
this.staticQuillConfig.toolbar.container.splice(-1, 0, [{ placeholder }]);
|
|
2364
|
+
}), mapTo(true), take(1));
|
|
2365
|
+
this.textChanged$ = new Subject();
|
|
2366
|
+
this.componentDestroyed$ = new Subject();
|
|
2367
|
+
}
|
|
2368
|
+
getTextStyles() {
|
|
2369
|
+
const { color, font, lineHeight, padding } = this.block.options;
|
|
2370
|
+
return Object.assign(Object.assign(Object.assign({ color, 'word-break': 'break-all' }, createLineHeight(lineHeight)), createFont(font)), createPadding(padding));
|
|
2371
|
+
}
|
|
2372
|
+
contentChanged() {
|
|
2373
|
+
this.textChanged$.next();
|
|
2374
|
+
}
|
|
2375
|
+
ngOnInit() {
|
|
2376
|
+
this.textChanged$.pipe(debounceTime(600), takeUntil(this.componentDestroyed$)).subscribe(() => {
|
|
2377
|
+
this.emailObject.markForCheck();
|
|
2378
|
+
});
|
|
2379
|
+
}
|
|
2380
|
+
ngOnDestroy() {
|
|
2381
|
+
this.componentDestroyed$.next();
|
|
2382
|
+
this.componentDestroyed$.complete();
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
TextElementComponent.decorators = [
|
|
2386
|
+
{ type: Component, args: [{
|
|
2387
|
+
selector: 'pb-text-element',
|
|
2388
|
+
template: "<!--\n ~ Copyright (c) 2024 Pobuca.\n ~ All rights reserved.\n -->\n\n<div [ngStyle]=\"getTextStyles()\">\n <quill-editor *ngIf=\"quillInit$ | async; else loadingModules\" i18n-placeholder=\"text-element|placeholder\"\n placeholder=\"Insert text here ...\" [modules]=\"staticQuillConfig\" [readOnly]=\"setAsReadOnly$ |async\"\n [(ngModel)]=\"block.innerText\" (onContentChanged)=\"contentChanged()\">\n </quill-editor>\n</div>\n\n<ng-template #loadingModules>\n <span i18n=\"text-element|loading message\">Please wait ...</span>\n</ng-template>\n",
|
|
2389
|
+
encapsulation: ViewEncapsulation.None,
|
|
2390
|
+
styles: ["@charset \"UTF-8\";\n/*!\n * Copyright (c) 2024 Pobuca.\n * All rights reserved.\n */\n/*!\n * Quill Editor v1.3.7\n * https://quilljs.com/\n * Copyright (c) 2014, Jason Chen\n * Copyright (c) 2013, salesforce.com\n */.ql-container{box-sizing:border-box;font-family:Helvetica,Arial,sans-serif;font-size:13px;height:100%;margin:0;position:relative}.ql-container.ql-disabled .ql-tooltip{visibility:hidden}.ql-container.ql-disabled .ql-editor ul[data-checked]>li:before{pointer-events:none}.ql-clipboard{left:-100000px;height:1px;overflow-y:hidden;position:absolute;top:50%}.ql-clipboard p{margin:0;padding:0}.ql-editor{box-sizing:border-box;line-height:1.42;height:100%;outline:none;overflow-y:auto;padding:12px 15px;-o-tab-size:4;tab-size:4;-moz-tab-size:4;text-align:left;white-space:pre-wrap;word-wrap:break-word}.ql-editor>*{cursor:text}.ql-editor blockquote,.ql-editor h1,.ql-editor h2,.ql-editor h3,.ql-editor h4,.ql-editor h5,.ql-editor h6,.ql-editor ol,.ql-editor p,.ql-editor pre,.ql-editor ul{margin:0;padding:0;counter-reset:list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9}.ql-editor ol,.ql-editor ul{padding-left:1.5em}.ql-editor ol>li,.ql-editor ul>li{list-style-type:none}.ql-editor ul>li:before{content:\"\u2022\"}.ql-editor ul[data-checked=false],.ql-editor ul[data-checked=true]{pointer-events:none}.ql-editor ul[data-checked=false]>li *,.ql-editor ul[data-checked=true]>li *{pointer-events:all}.ql-editor ul[data-checked=false]>li:before,.ql-editor ul[data-checked=true]>li:before{color:#777;cursor:pointer;pointer-events:all}.ql-editor ul[data-checked=true]>li:before{content:\"\u2611\"}.ql-editor ul[data-checked=false]>li:before{content:\"\u2610\"}.ql-editor li:before{display:inline-block;white-space:nowrap;width:1.2em}.ql-editor li:not(.ql-direction-rtl):before{margin-left:-1.5em;margin-right:.3em;text-align:right}.ql-editor li.ql-direction-rtl:before{margin-left:.3em;margin-right:-1.5em}.ql-editor ol li:not(.ql-direction-rtl),.ql-editor ul li:not(.ql-direction-rtl){padding-left:1.5em}.ql-editor ol li.ql-direction-rtl,.ql-editor ul li.ql-direction-rtl{padding-right:1.5em}.ql-editor ol li{counter-reset:list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;counter-increment:list-0}.ql-editor ol li:before{content:counter(list-0,decimal) \". \"}.ql-editor ol li.ql-indent-1{counter-increment:list-1}.ql-editor ol li.ql-indent-1:before{content:counter(list-1,lower-alpha) \". \"}.ql-editor ol li.ql-indent-1{counter-reset:list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9}.ql-editor ol li.ql-indent-2{counter-increment:list-2}.ql-editor ol li.ql-indent-2:before{content:counter(list-2,lower-roman) \". \"}.ql-editor ol li.ql-indent-2{counter-reset:list-3 list-4 list-5 list-6 list-7 list-8 list-9}.ql-editor ol li.ql-indent-3{counter-increment:list-3}.ql-editor ol li.ql-indent-3:before{content:counter(list-3,decimal) \". \"}.ql-editor ol li.ql-indent-3{counter-reset:list-4 list-5 list-6 list-7 list-8 list-9}.ql-editor ol li.ql-indent-4{counter-increment:list-4}.ql-editor ol li.ql-indent-4:before{content:counter(list-4,lower-alpha) \". \"}.ql-editor ol li.ql-indent-4{counter-reset:list-5 list-6 list-7 list-8 list-9}.ql-editor ol li.ql-indent-5{counter-increment:list-5}.ql-editor ol li.ql-indent-5:before{content:counter(list-5,lower-roman) \". \"}.ql-editor ol li.ql-indent-5{counter-reset:list-6 list-7 list-8 list-9}.ql-editor ol li.ql-indent-6{counter-increment:list-6}.ql-editor ol li.ql-indent-6:before{content:counter(list-6,decimal) \". \"}.ql-editor ol li.ql-indent-6{counter-reset:list-7 list-8 list-9}.ql-editor ol li.ql-indent-7{counter-increment:list-7}.ql-editor ol li.ql-indent-7:before{content:counter(list-7,lower-alpha) \". \"}.ql-editor ol li.ql-indent-7{counter-reset:list-8 list-9}.ql-editor ol li.ql-indent-8{counter-increment:list-8}.ql-editor ol li.ql-indent-8:before{content:counter(list-8,lower-roman) \". \"}.ql-editor ol li.ql-indent-8{counter-reset:list-9}.ql-editor ol li.ql-indent-9{counter-increment:list-9}.ql-editor ol li.ql-indent-9:before{content:counter(list-9,decimal) \". \"}.ql-editor .ql-indent-1:not(.ql-direction-rtl){padding-left:3em}.ql-editor li.ql-indent-1:not(.ql-direction-rtl){padding-left:4.5em}.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right{padding-right:3em}.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right{padding-right:4.5em}.ql-editor .ql-indent-2:not(.ql-direction-rtl){padding-left:6em}.ql-editor li.ql-indent-2:not(.ql-direction-rtl){padding-left:7.5em}.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right{padding-right:6em}.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right{padding-right:7.5em}.ql-editor .ql-indent-3:not(.ql-direction-rtl){padding-left:9em}.ql-editor li.ql-indent-3:not(.ql-direction-rtl){padding-left:10.5em}.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right{padding-right:9em}.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right{padding-right:10.5em}.ql-editor .ql-indent-4:not(.ql-direction-rtl){padding-left:12em}.ql-editor li.ql-indent-4:not(.ql-direction-rtl){padding-left:13.5em}.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right{padding-right:12em}.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right{padding-right:13.5em}.ql-editor .ql-indent-5:not(.ql-direction-rtl){padding-left:15em}.ql-editor li.ql-indent-5:not(.ql-direction-rtl){padding-left:16.5em}.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right{padding-right:15em}.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right{padding-right:16.5em}.ql-editor .ql-indent-6:not(.ql-direction-rtl){padding-left:18em}.ql-editor li.ql-indent-6:not(.ql-direction-rtl){padding-left:19.5em}.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right{padding-right:18em}.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right{padding-right:19.5em}.ql-editor .ql-indent-7:not(.ql-direction-rtl){padding-left:21em}.ql-editor li.ql-indent-7:not(.ql-direction-rtl){padding-left:22.5em}.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right{padding-right:21em}.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right{padding-right:22.5em}.ql-editor .ql-indent-8:not(.ql-direction-rtl){padding-left:24em}.ql-editor li.ql-indent-8:not(.ql-direction-rtl){padding-left:25.5em}.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right{padding-right:24em}.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right{padding-right:25.5em}.ql-editor .ql-indent-9:not(.ql-direction-rtl){padding-left:27em}.ql-editor li.ql-indent-9:not(.ql-direction-rtl){padding-left:28.5em}.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right{padding-right:27em}.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right{padding-right:28.5em}.ql-editor .ql-video{display:block;max-width:100%}.ql-editor .ql-video.ql-align-center{margin:0 auto}.ql-editor .ql-video.ql-align-right{margin:0 0 0 auto}.ql-editor .ql-bg-black{background-color:#000}.ql-editor .ql-bg-red{background-color:#e60000}.ql-editor .ql-bg-orange{background-color:#f90}.ql-editor .ql-bg-yellow{background-color:#ff0}.ql-editor .ql-bg-green{background-color:#008a00}.ql-editor .ql-bg-blue{background-color:#06c}.ql-editor .ql-bg-purple{background-color:#93f}.ql-editor .ql-color-white{color:#fff}.ql-editor .ql-color-red{color:#e60000}.ql-editor .ql-color-orange{color:#f90}.ql-editor .ql-color-yellow{color:#ff0}.ql-editor .ql-color-green{color:#008a00}.ql-editor .ql-color-blue{color:#06c}.ql-editor .ql-color-purple{color:#93f}.ql-editor .ql-font-serif{font-family:Georgia,Times New Roman,serif}.ql-editor .ql-font-monospace{font-family:Monaco,Courier New,monospace}.ql-editor .ql-size-small{font-size:.75em}.ql-editor .ql-size-large{font-size:1.5em}.ql-editor .ql-size-huge{font-size:2.5em}.ql-editor .ql-direction-rtl{direction:rtl;text-align:inherit}.ql-editor .ql-align-center{text-align:center}.ql-editor .ql-align-justify{text-align:justify}.ql-editor .ql-align-right{text-align:right}.ql-editor.ql-blank:before{color:rgba(0,0,0,.6);content:attr(data-placeholder);font-style:italic;left:15px;pointer-events:none;position:absolute;right:15px}.ql-bubble.ql-toolbar:after,.ql-bubble .ql-toolbar:after{clear:both;content:\"\";display:table}.ql-bubble.ql-toolbar button,.ql-bubble .ql-toolbar button{background:none;border:none;cursor:pointer;display:inline-block;float:left;height:24px;padding:3px 5px;width:28px}.ql-bubble.ql-toolbar button svg,.ql-bubble .ql-toolbar button svg{float:left;height:100%}.ql-bubble.ql-toolbar button:active:hover,.ql-bubble .ql-toolbar button:active:hover{outline:none}.ql-bubble.ql-toolbar input.ql-image[type=file],.ql-bubble .ql-toolbar input.ql-image[type=file]{display:none}.ql-bubble.ql-toolbar .ql-picker-item.ql-selected,.ql-bubble .ql-toolbar .ql-picker-item.ql-selected,.ql-bubble.ql-toolbar .ql-picker-item:hover,.ql-bubble .ql-toolbar .ql-picker-item:hover,.ql-bubble.ql-toolbar .ql-picker-label.ql-active,.ql-bubble .ql-toolbar .ql-picker-label.ql-active,.ql-bubble.ql-toolbar .ql-picker-label:hover,.ql-bubble .ql-toolbar .ql-picker-label:hover,.ql-bubble.ql-toolbar button.ql-active,.ql-bubble .ql-toolbar button.ql-active,.ql-bubble.ql-toolbar button:focus,.ql-bubble .ql-toolbar button:focus,.ql-bubble.ql-toolbar button:hover,.ql-bubble .ql-toolbar button:hover{color:#fff}.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-fill,.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-fill,.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill,.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill,.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-fill,.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-fill,.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-fill,.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-fill,.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-fill,.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-fill,.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,.ql-bubble.ql-toolbar button.ql-active .ql-fill,.ql-bubble .ql-toolbar button.ql-active .ql-fill,.ql-bubble.ql-toolbar button.ql-active .ql-stroke.ql-fill,.ql-bubble .ql-toolbar button.ql-active .ql-stroke.ql-fill,.ql-bubble.ql-toolbar button:focus .ql-fill,.ql-bubble .ql-toolbar button:focus .ql-fill,.ql-bubble.ql-toolbar button:focus .ql-stroke.ql-fill,.ql-bubble .ql-toolbar button:focus .ql-stroke.ql-fill,.ql-bubble.ql-toolbar button:hover .ql-fill,.ql-bubble .ql-toolbar button:hover .ql-fill,.ql-bubble.ql-toolbar button:hover .ql-stroke.ql-fill,.ql-bubble .ql-toolbar button:hover .ql-stroke.ql-fill{fill:#fff}.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-stroke,.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-stroke,.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter,.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter,.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-stroke,.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-stroke,.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-stroke-miter,.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-stroke-miter,.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-stroke,.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-stroke,.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-stroke,.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-stroke,.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-stroke-miter,.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-stroke-miter,.ql-bubble.ql-toolbar button.ql-active .ql-stroke,.ql-bubble .ql-toolbar button.ql-active .ql-stroke,.ql-bubble.ql-toolbar button.ql-active .ql-stroke-miter,.ql-bubble .ql-toolbar button.ql-active .ql-stroke-miter,.ql-bubble.ql-toolbar button:focus .ql-stroke,.ql-bubble .ql-toolbar button:focus .ql-stroke,.ql-bubble.ql-toolbar button:focus .ql-stroke-miter,.ql-bubble .ql-toolbar button:focus .ql-stroke-miter,.ql-bubble.ql-toolbar button:hover .ql-stroke,.ql-bubble .ql-toolbar button:hover .ql-stroke,.ql-bubble.ql-toolbar button:hover .ql-stroke-miter,.ql-bubble .ql-toolbar button:hover .ql-stroke-miter{stroke:#fff}@media (pointer:coarse){.ql-bubble.ql-toolbar button:hover:not(.ql-active),.ql-bubble .ql-toolbar button:hover:not(.ql-active){color:#ccc}.ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-fill,.ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-fill,.ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill,.ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill{fill:#ccc}.ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-stroke,.ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-stroke,.ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter,.ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter{stroke:#ccc}}.ql-bubble,.ql-bubble *{box-sizing:border-box}.ql-bubble .ql-hidden{display:none}.ql-bubble .ql-out-bottom,.ql-bubble .ql-out-top{visibility:hidden}.ql-bubble .ql-tooltip{position:absolute;transform:translateY(10px)}.ql-bubble .ql-tooltip a{cursor:pointer;text-decoration:none}.ql-bubble .ql-tooltip.ql-flip{transform:translateY(-10px)}.ql-bubble .ql-formats{display:inline-block;vertical-align:middle}.ql-bubble .ql-formats:after{clear:both;content:\"\";display:table}.ql-bubble .ql-stroke{fill:none;stroke:#ccc;stroke-linecap:round;stroke-linejoin:round;stroke-width:2}.ql-bubble .ql-stroke-miter{fill:none;stroke:#ccc;stroke-miterlimit:10;stroke-width:2}.ql-bubble .ql-fill,.ql-bubble .ql-stroke.ql-fill{fill:#ccc}.ql-bubble .ql-empty{fill:none}.ql-bubble .ql-even{fill-rule:evenodd}.ql-bubble .ql-stroke.ql-thin,.ql-bubble .ql-thin{stroke-width:1}.ql-bubble .ql-transparent{opacity:.4}.ql-bubble .ql-direction svg:last-child{display:none}.ql-bubble .ql-direction.ql-active svg:last-child{display:inline}.ql-bubble .ql-direction.ql-active svg:first-child{display:none}.ql-bubble .ql-editor h1{font-size:2em}.ql-bubble .ql-editor h2{font-size:1.5em}.ql-bubble .ql-editor h3{font-size:1.17em}.ql-bubble .ql-editor h4{font-size:1em}.ql-bubble .ql-editor h5{font-size:.83em}.ql-bubble .ql-editor h6{font-size:.67em}.ql-bubble .ql-editor a{text-decoration:underline}.ql-bubble .ql-editor blockquote{border-left:4px solid #ccc;margin-bottom:5px;margin-top:5px;padding-left:16px}.ql-bubble .ql-editor code,.ql-bubble .ql-editor pre{background-color:#f0f0f0;border-radius:3px}.ql-bubble .ql-editor pre{white-space:pre-wrap;margin-bottom:5px;margin-top:5px;padding:5px 10px}.ql-bubble .ql-editor code{font-size:85%;padding:2px 4px}.ql-bubble .ql-editor pre.ql-syntax{background-color:#23241f;color:#f8f8f2;overflow:visible}.ql-bubble .ql-editor img{max-width:100%}.ql-bubble .ql-picker{color:#ccc;display:inline-block;float:left;font-size:14px;font-weight:500;height:24px;position:relative;vertical-align:middle}.ql-bubble .ql-picker-label{cursor:pointer;display:inline-block;height:100%;padding-left:8px;padding-right:2px;position:relative;width:100%}.ql-bubble .ql-picker-label:before{display:inline-block;line-height:22px}.ql-bubble .ql-picker-options{background-color:#444;display:none;min-width:100%;padding:4px 8px;position:absolute;white-space:nowrap}.ql-bubble .ql-picker-options .ql-picker-item{cursor:pointer;display:block;padding-bottom:5px;padding-top:5px}.ql-bubble .ql-picker.ql-expanded .ql-picker-label{color:#777;z-index:2}.ql-bubble .ql-picker.ql-expanded .ql-picker-label .ql-fill{fill:#777}.ql-bubble .ql-picker.ql-expanded .ql-picker-label .ql-stroke{stroke:#777}.ql-bubble .ql-picker.ql-expanded .ql-picker-options{display:block;margin-top:-1px;top:100%;z-index:1}.ql-bubble .ql-color-picker,.ql-bubble .ql-icon-picker{width:28px}.ql-bubble .ql-color-picker .ql-picker-label,.ql-bubble .ql-icon-picker .ql-picker-label{padding:2px 4px}.ql-bubble .ql-color-picker .ql-picker-label svg,.ql-bubble .ql-icon-picker .ql-picker-label svg{right:4px}.ql-bubble .ql-icon-picker .ql-picker-options{padding:4px 0}.ql-bubble .ql-icon-picker .ql-picker-item{height:24px;width:24px;padding:2px 4px}.ql-bubble .ql-color-picker .ql-picker-options{padding:3px 5px;width:152px}.ql-bubble .ql-color-picker .ql-picker-item{border:1px solid transparent;float:left;height:16px;margin:2px;padding:0;width:16px}.ql-bubble .ql-picker:not(.ql-color-picker):not(.ql-icon-picker) svg{position:absolute;margin-top:-9px;right:0;top:50%;width:18px}.ql-bubble .ql-picker.ql-font .ql-picker-item[data-label]:not([data-label=\"\"]):before,.ql-bubble .ql-picker.ql-font .ql-picker-label[data-label]:not([data-label=\"\"]):before,.ql-bubble .ql-picker.ql-header .ql-picker-item[data-label]:not([data-label=\"\"]):before,.ql-bubble .ql-picker.ql-header .ql-picker-label[data-label]:not([data-label=\"\"]):before,.ql-bubble .ql-picker.ql-size .ql-picker-item[data-label]:not([data-label=\"\"]):before,.ql-bubble .ql-picker.ql-size .ql-picker-label[data-label]:not([data-label=\"\"]):before{content:attr(data-label)}.ql-bubble .ql-picker.ql-header{width:98px}.ql-bubble .ql-picker.ql-header .ql-picker-item:before,.ql-bubble .ql-picker.ql-header .ql-picker-label:before{content:\"Normal\"}.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"1\"]:before,.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value=\"1\"]:before{content:\"Heading 1\"}.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"2\"]:before,.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value=\"2\"]:before{content:\"Heading 2\"}.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"3\"]:before,.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value=\"3\"]:before{content:\"Heading 3\"}.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"4\"]:before,.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value=\"4\"]:before{content:\"Heading 4\"}.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"5\"]:before,.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value=\"5\"]:before{content:\"Heading 5\"}.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"6\"]:before,.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value=\"6\"]:before{content:\"Heading 6\"}.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"1\"]:before{font-size:2em}.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"2\"]:before{font-size:1.5em}.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"3\"]:before{font-size:1.17em}.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"4\"]:before{font-size:1em}.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"5\"]:before{font-size:.83em}.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value=\"6\"]:before{font-size:.67em}.ql-bubble .ql-picker.ql-font{width:108px}.ql-bubble .ql-picker.ql-font .ql-picker-item:before,.ql-bubble .ql-picker.ql-font .ql-picker-label:before{content:\"Sans Serif\"}.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=serif]:before,.ql-bubble .ql-picker.ql-font .ql-picker-label[data-value=serif]:before{content:\"Serif\"}.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=monospace]:before,.ql-bubble .ql-picker.ql-font .ql-picker-label[data-value=monospace]:before{content:\"Monospace\"}.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=serif]:before{font-family:Georgia,Times New Roman,serif}.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=monospace]:before{font-family:Monaco,Courier New,monospace}.ql-bubble .ql-picker.ql-size{width:98px}.ql-bubble .ql-picker.ql-size .ql-picker-item:before,.ql-bubble .ql-picker.ql-size .ql-picker-label:before{content:\"Normal\"}.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=small]:before,.ql-bubble .ql-picker.ql-size .ql-picker-label[data-value=small]:before{content:\"Small\"}.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=large]:before,.ql-bubble .ql-picker.ql-size .ql-picker-label[data-value=large]:before{content:\"Large\"}.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=huge]:before,.ql-bubble .ql-picker.ql-size .ql-picker-label[data-value=huge]:before{content:\"Huge\"}.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=small]:before{font-size:10px}.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=large]:before{font-size:18px}.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=huge]:before{font-size:32px}.ql-bubble .ql-color-picker.ql-background .ql-picker-item{background-color:#fff}.ql-bubble .ql-color-picker.ql-color .ql-picker-item{background-color:#000}.ql-bubble .ql-toolbar .ql-formats{margin:8px 12px 8px 0}.ql-bubble .ql-toolbar .ql-formats:first-child{margin-left:12px}.ql-bubble .ql-color-picker svg{margin:1px}.ql-bubble .ql-color-picker .ql-picker-item.ql-selected,.ql-bubble .ql-color-picker .ql-picker-item:hover{border-color:#fff}.ql-bubble .ql-tooltip{background-color:#444;border-radius:25px;color:#fff}.ql-bubble .ql-tooltip-arrow{border-left:6px solid transparent;border-right:6px solid transparent;content:\" \";display:block;left:50%;margin-left:-6px;position:absolute}.ql-bubble .ql-tooltip:not(.ql-flip) .ql-tooltip-arrow{border-bottom:6px solid #444;top:-6px}.ql-bubble .ql-tooltip.ql-flip .ql-tooltip-arrow{border-top:6px solid #444;bottom:-6px}.ql-bubble .ql-tooltip.ql-editing .ql-tooltip-editor{display:block}.ql-bubble .ql-tooltip.ql-editing .ql-formats{visibility:hidden}.ql-bubble .ql-tooltip-editor{display:none}.ql-bubble .ql-tooltip-editor input[type=text]{background:transparent;border:none;color:#fff;font-size:13px;height:100%;outline:none;padding:10px 20px;position:absolute;width:100%}.ql-bubble .ql-tooltip-editor a{top:10px;position:absolute;right:20px}.ql-bubble .ql-tooltip-editor a:before{color:#ccc;content:\"\u00D7\";font-size:16px;font-weight:700}.ql-container.ql-bubble:not(.ql-disabled) a{position:relative;white-space:nowrap}.ql-container.ql-bubble:not(.ql-disabled) a:before{background-color:#444;border-radius:15px;top:-5px;font-size:12px;color:#fff;content:attr(href);font-weight:400;overflow:hidden;padding:5px 15px;text-decoration:none;z-index:1}.ql-container.ql-bubble:not(.ql-disabled) a:after{border-top:6px solid #444;border-left:6px solid transparent;border-right:6px solid transparent;top:0;content:\" \";height:0;width:0}.ql-container.ql-bubble:not(.ql-disabled) a:after,.ql-container.ql-bubble:not(.ql-disabled) a:before{left:0;margin-left:50%;position:absolute;transform:translate(-50%,-100%);transition:visibility 0s ease .2s;visibility:hidden}.ql-container.ql-bubble:not(.ql-disabled) a:hover:after,.ql-container.ql-bubble:not(.ql-disabled) a:hover:before{visibility:visible}quill-editor{display:block;word-break:break-word}quill-editor .ql-container{font-family:inherit;font-size:inherit}quill-editor .ql-container .ql-editor{padding:0!important;line-height:inherit;font:inherit;overflow:visible}quill-editor .ql-placeholder .ql-picker-label:before{display:block;content:\"Tags\";min-width:50px}quill-editor .ql-placeholder.ql-expanded .ql-picker-item:before{content:attr(data-value);width:-moz-min-content;width:min-content}quill-editor .ql-tooltip{z-index:10;line-height:normal}quill-editor .ql-tooltip .ql-picker-options{max-height:300px;overflow:auto}quill-editor .ql-toolbar{width:600px;width:-moz-max-content;width:max-content}quill-editor h1,quill-editor h2,quill-editor h3,quill-editor h4,quill-editor h5,quill-editor h6{font-weight:inherit;line-height:inherit}"]
|
|
2391
|
+
},] }
|
|
2392
|
+
];
|
|
2393
|
+
TextElementComponent.ctorParameters = () => [
|
|
2394
|
+
{ type: PbUserRestApiService },
|
|
2395
|
+
{ type: PbEmailObjectStoreService },
|
|
2396
|
+
{ type: PbUserInterfaceService }
|
|
2397
|
+
];
|
|
2398
|
+
|
|
2399
|
+
/*
|
|
2400
|
+
* Copyright (c) 2024 Pobuca.
|
|
2401
|
+
* All rights reserved.
|
|
2402
|
+
*/
|
|
2403
|
+
class ImageComponent extends AbstractBlock {
|
|
2404
|
+
constructor(imageUploader, chRef, ngb) {
|
|
2405
|
+
super();
|
|
2406
|
+
this.imageUploader = imageUploader;
|
|
2407
|
+
this.chRef = chRef;
|
|
2408
|
+
this.ngb = ngb;
|
|
2409
|
+
}
|
|
2410
|
+
// @Input() block = new ImageBlock();
|
|
2411
|
+
get align() {
|
|
2412
|
+
return this.block.options.align;
|
|
2413
|
+
}
|
|
2414
|
+
get src() {
|
|
2415
|
+
return this.block.src || 'https://via.placeholder.com/600x200?text=CHANGE+ME';
|
|
2416
|
+
}
|
|
2417
|
+
uploadImage() {
|
|
2418
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2419
|
+
const src = yield this.imageUploader.browse$().toPromise();
|
|
2420
|
+
if (src) {
|
|
2421
|
+
this.block.src = src;
|
|
2422
|
+
this.chRef.markForCheck();
|
|
2423
|
+
}
|
|
2424
|
+
});
|
|
2425
|
+
}
|
|
2426
|
+
getImageStyles() {
|
|
2427
|
+
const { border, width, height, padding } = this.block.options;
|
|
2428
|
+
return Object.assign(Object.assign({ width: createWidthHeight(width), height: createWidthHeight(height) }, createPadding(padding)), createBorder(border));
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
ImageComponent.decorators = [
|
|
2432
|
+
{ type: Component, args: [{
|
|
2433
|
+
selector: 'pb-image',
|
|
2434
|
+
template: "<!--\n ~ Copyright (c) 2024 Pobuca.\n ~ All rights reserved.\n -->\n\n<!-- <div\n class=\"overflow\"\n *ngIf=\"(ngb.currentEditingBlock$ | async) !== block\"\n (click)=\"uploadImage()\"\n>\n <mat-icon inline>cloud_upload</mat-icon>\n</div> -->\n<img [src]=\"src\" [title]=\"block.options.title\" [ngStyle]=\"getImageStyles()\" />\n",
|
|
2435
|
+
styles: [":host{display:block;line-height:0}:host:hover .overflow{opacity:1}.overflow{position:absolute;top:0;right:0;bottom:0;left:0;background-color:rgba(0,0,0,.3);opacity:0;will-change:opacity;transition:opacity .3s cubic-bezier(.075,.82,.165,1);cursor:pointer;color:#fff;display:flex;align-items:center;justify-content:center;font-size:3em}.overflow mat-icon{opacity:.8}img{max-width:100%;box-sizing:border-box}"]
|
|
2436
|
+
},] }
|
|
2437
|
+
];
|
|
2438
|
+
ImageComponent.ctorParameters = () => [
|
|
2439
|
+
{ type: PbUserImageUploaderService },
|
|
2440
|
+
{ type: ChangeDetectorRef },
|
|
2441
|
+
{ type: PbEmailBuilderService }
|
|
2442
|
+
];
|
|
2443
|
+
ImageComponent.propDecorators = {
|
|
2444
|
+
align: [{ type: HostBinding, args: ['style.textAlign',] }]
|
|
2445
|
+
};
|
|
2446
|
+
|
|
2447
|
+
/*
|
|
2448
|
+
* Copyright (c) 2024 Pobuca.
|
|
2449
|
+
* All rights reserved.
|
|
2450
|
+
*/
|
|
2451
|
+
class ButtonComponent extends AbstractBlock {
|
|
2452
|
+
// @Input() block: ButtonBlock;
|
|
2453
|
+
getButtonStyles() {
|
|
2454
|
+
const { backgroundColor, border, color, font, lineHeight, innerPadding, fullWidth } = this.block.options;
|
|
2455
|
+
return Object.assign(Object.assign(Object.assign(Object.assign({ color, width: fullWidth ? '100%' : 'auto', backgroundColor }, createFont(font)), createPadding(innerPadding)), createBorder(border)), createLineHeight(lineHeight));
|
|
2456
|
+
}
|
|
2457
|
+
getParentStyles() {
|
|
2458
|
+
const { align, padding } = this.block.options;
|
|
2459
|
+
return Object.assign({ justifyContent: (align === 'center' && 'center') ||
|
|
2460
|
+
(align === 'right' && 'flex-end') ||
|
|
2461
|
+
'flex-start' }, createPadding(padding));
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
ButtonComponent.decorators = [
|
|
2465
|
+
{ type: Component, args: [{
|
|
2466
|
+
selector: 'pb-button',
|
|
2467
|
+
template: "<!--\n ~ Copyright (c) 2024 Pobuca.\n ~ All rights reserved.\n -->\n\n<div fxLayout [ngStyle]=\"getParentStyles()\">\n <button type=\"button\" [class.full-width]=\"block.options.fullWidth\" [ngStyle]=\"getButtonStyles()\">\n {{ block.innerText }}\n </button>\n</div>\n",
|
|
2468
|
+
encapsulation: ViewEncapsulation.Emulated,
|
|
2469
|
+
styles: ["button{transition:all .5s cubic-bezier(.445,.05,.55,.95)}"]
|
|
2470
|
+
},] }
|
|
2471
|
+
];
|
|
2472
|
+
|
|
2473
|
+
/*
|
|
2474
|
+
* Copyright (c) 2024 Pobuca.
|
|
2475
|
+
* All rights reserved.
|
|
2476
|
+
*/
|
|
2477
|
+
class DividerComponent extends AbstractBlock {
|
|
2478
|
+
// @Input()
|
|
2479
|
+
// block: DividerBlock = new DividerBlock();
|
|
2480
|
+
getDividerStyles() {
|
|
2481
|
+
const { border, padding } = this.block.options;
|
|
2482
|
+
return Object.assign(Object.assign({}, createBorder(border, 'borderTop')), createPadding(padding, 'margin'));
|
|
2483
|
+
}
|
|
2484
|
+
ngOnInit() {
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
DividerComponent.decorators = [
|
|
2488
|
+
{ type: Component, args: [{
|
|
2489
|
+
selector: 'pb-divider',
|
|
2490
|
+
template: '<div [ngStyle]="getDividerStyles()"></div>',
|
|
2491
|
+
styles: [`
|
|
2492
|
+
:host {
|
|
2493
|
+
display: table;
|
|
2494
|
+
width: 100%;
|
|
2495
|
+
}
|
|
2496
|
+
`]
|
|
2497
|
+
},] }
|
|
2498
|
+
];
|
|
2499
|
+
|
|
2500
|
+
/*
|
|
2501
|
+
* Copyright (c) 2024 Pobuca.
|
|
2502
|
+
* All rights reserved.
|
|
2503
|
+
*/
|
|
2504
|
+
class SpacerComponent extends AbstractBlock {
|
|
2505
|
+
// @Input() block: SpacerBlock = new SpacerBlock();
|
|
2506
|
+
get height() {
|
|
2507
|
+
return createWidthHeight(this.block.options.height);
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
SpacerComponent.decorators = [
|
|
2511
|
+
{ type: Component, args: [{
|
|
2512
|
+
selector: 'pb-spacer',
|
|
2513
|
+
template: '',
|
|
2514
|
+
styles: [`
|
|
2515
|
+
:host {
|
|
2516
|
+
display: table;
|
|
2517
|
+
width: 100%;
|
|
2518
|
+
transition: all 500ms cubic-bezier(0.445, 0.05, 0.55, 0.95);
|
|
2519
|
+
}
|
|
2520
|
+
`]
|
|
2521
|
+
},] }
|
|
2522
|
+
];
|
|
2523
|
+
SpacerComponent.propDecorators = {
|
|
2524
|
+
height: [{ type: HostBinding, args: ['style.height',] }]
|
|
2525
|
+
};
|
|
2526
|
+
|
|
2527
|
+
/*
|
|
2528
|
+
* Copyright (c) 2024 Pobuca.
|
|
2529
|
+
* All rights reserved.
|
|
2530
|
+
*/
|
|
2531
|
+
class SocialComponent extends AbstractBlock {
|
|
2532
|
+
// @Input()
|
|
2533
|
+
// block: SocialBlock;
|
|
2534
|
+
getParentStyles() {
|
|
2535
|
+
const { color, font, lineHeight, padding } = this.block.options;
|
|
2536
|
+
return Object.assign(Object.assign(Object.assign({ color }, createLineHeight(lineHeight)), createFont(font)), createPadding(padding));
|
|
2537
|
+
}
|
|
2538
|
+
getLabelStyles() {
|
|
2539
|
+
const { innerPadding } = this.block.options;
|
|
2540
|
+
return Object.assign(Object.assign({}, createPadding(innerPadding)), { lineHeight: 0 });
|
|
2541
|
+
}
|
|
2542
|
+
getSocialListStyles() {
|
|
2543
|
+
const { align } = this.block.options;
|
|
2544
|
+
return {
|
|
2545
|
+
display: 'flex',
|
|
2546
|
+
placeContent: (align === 'left' && 'flex-start') ||
|
|
2547
|
+
(align === 'right' && 'flex-end') ||
|
|
2548
|
+
align
|
|
2549
|
+
};
|
|
2550
|
+
}
|
|
2551
|
+
getSocialListClasses() {
|
|
2552
|
+
return `social-list ${this.block.options.mode}`;
|
|
2553
|
+
}
|
|
2554
|
+
getSocialNetworkIcon(network) {
|
|
2555
|
+
return getAssetByPath(`${network}.png`);
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
SocialComponent.decorators = [
|
|
2559
|
+
{ type: Component, args: [{
|
|
2560
|
+
selector: 'pb-social',
|
|
2561
|
+
template: "<!--\n ~ Copyright (c) 2024 Pobuca.\n ~ All rights reserved.\n -->\n\n<div fxLayout [ngStyle]=\"getParentStyles()\">\n <div [class]=\"getSocialListClasses()\" [ngStyle]=\"getSocialListStyles()\">\n <ng-container *ngIf=\"block.networks.length; else noSocialNetworks\">\n <div class=\"social-list-item\" *ngFor=\"let network of block.networks\">\n <div [ngStyle]=\"getLabelStyles()\">\n <img [src]=\"getSocialNetworkIcon(network.name)\" [width]=\"block.options.iconSize.value\"\n [height]=\"block.options.iconSize.value\" [classList]=\"network.name\" [alt]=\"network.name\" />\n </div>\n <span *ngIf=\"network.label\">{{ network.label }}</span>\n </div>\n </ng-container>\n </div>\n</div>\n\n<ng-template #noSocialNetworks>\n <p i18n=\"social block|Empty social list message\">Please add some social networks.</p>\n</ng-template>\n",
|
|
2562
|
+
styles: ["/*!\n * Copyright (c) 2024 Pobuca.\n * All rights reserved.\n */:host{display:block}p{margin:0}.social-list{display:flex;width:100%;flex-wrap:wrap}.social-list.horizontal{flex-direction:row}.social-list.vertical{flex-direction:column}.social-list-item{display:flex;flex-direction:row;align-items:center}.social-list-item img{border-radius:3px}.social-list-item img.facebook{background-color:#3a5898}.social-list-item img.twitter{background-color:#d95988}.social-list-item img.github{background-color:#000}.social-list-item img.instagram{background-color:#3f729b}.social-list-item img.web{background-color:#4bade9}.social-list-item img.snapchat{background-color:#fffa54}.social-list-item img.youtube{background-color:#ea3323}.social-list-item img.vimeo{background-color:#53b4e7}.social-list-item img.medium{background-color:#000}.social-list-item img.soundcloud{background-color:#ef7f31}.social-list-item img.dribbble{background-color:#d95988}.social-list-item img.pinterest{background-color:#bd071c}.social-list-item img.linkedin{background-color:#0077b5}.social-list-item img.tumblr{background-color:#334356}.social-list-item img.xing{background-color:#296365}.social-list-item img.tiktok{background-color:#000}"]
|
|
2563
|
+
},] }
|
|
2564
|
+
];
|
|
2565
|
+
|
|
2566
|
+
/*
|
|
2567
|
+
* Copyright (c) 2024 Pobuca.
|
|
2568
|
+
* All rights reserved.
|
|
2569
|
+
*/
|
|
2570
|
+
const cStore = new Map();
|
|
2571
|
+
/**
|
|
2572
|
+
* @internal
|
|
2573
|
+
*
|
|
2574
|
+
* Soon it must be optimized, and load all components lazily.
|
|
2575
|
+
*/
|
|
2576
|
+
class DynamicComponentDirective {
|
|
2577
|
+
constructor(cResolver, viewContainerRef) {
|
|
2578
|
+
this.cResolver = cResolver;
|
|
2579
|
+
this.viewContainerRef = viewContainerRef;
|
|
2580
|
+
cStore.set('text', TextElementComponent);
|
|
2581
|
+
cStore.set('image', ImageComponent);
|
|
2582
|
+
cStore.set('button', ButtonComponent);
|
|
2583
|
+
cStore.set('divider', DividerComponent);
|
|
2584
|
+
cStore.set('spacer', SpacerComponent);
|
|
2585
|
+
cStore.set('social', SocialComponent);
|
|
2586
|
+
}
|
|
2587
|
+
set pbDynamicComponent(block) {
|
|
2588
|
+
const component = this.cResolver.resolveComponentFactory(cStore.get(block.type));
|
|
2589
|
+
this.viewContainerRef.createComponent(component).instance.block = block;
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
DynamicComponentDirective.decorators = [
|
|
2593
|
+
{ type: Directive, args: [{
|
|
2594
|
+
selector: '[pbDynamicComponent]'
|
|
2595
|
+
},] }
|
|
2596
|
+
];
|
|
2597
|
+
DynamicComponentDirective.ctorParameters = () => [
|
|
2598
|
+
{ type: ComponentFactoryResolver },
|
|
2599
|
+
{ type: ViewContainerRef }
|
|
2600
|
+
];
|
|
2601
|
+
DynamicComponentDirective.propDecorators = {
|
|
2602
|
+
pbDynamicComponent: [{ type: Input }]
|
|
2603
|
+
};
|
|
2604
|
+
|
|
2605
|
+
/*
|
|
2606
|
+
* Copyright (c) 2024 Pobuca.
|
|
2607
|
+
* All rights reserved.
|
|
2608
|
+
*/
|
|
2609
|
+
// import { IpUserRestApiProvider } from '../providers';
|
|
2610
|
+
class BuilderContainerComponent {
|
|
2611
|
+
constructor(userInterfaceService, emailObjectStore, userRestApi, pbStorage, pbMiddlewaresService, sanitizer, userConfig) {
|
|
2612
|
+
var _a;
|
|
2613
|
+
this.userInterfaceService = userInterfaceService;
|
|
2614
|
+
this.emailObjectStore = emailObjectStore;
|
|
2615
|
+
this.userRestApi = userRestApi;
|
|
2616
|
+
this.pbStorage = pbStorage;
|
|
2617
|
+
this.pbMiddlewaresService = pbMiddlewaresService;
|
|
2618
|
+
this.sanitizer = sanitizer;
|
|
2619
|
+
this.userConfig = userConfig;
|
|
2620
|
+
this.showTemplateList = (_a = this.userConfig.templateListIfEmpty) !== null && _a !== void 0 ? _a : true;
|
|
2621
|
+
this.getStructures$ = this.emailObjectStore.emailStructuresAsObservable$;
|
|
2622
|
+
this.activeMatProgress$ = this.userInterfaceService.activeMatProgress$;
|
|
2623
|
+
}
|
|
2624
|
+
onHostClick() {
|
|
2625
|
+
this.userInterfaceService.editGeneralSettings();
|
|
2626
|
+
}
|
|
2627
|
+
trackBy(structure) {
|
|
2628
|
+
return structure.id;
|
|
2629
|
+
}
|
|
2630
|
+
disableStructureDrag$(structure) {
|
|
2631
|
+
return this.pbMiddlewaresService.disableStructureDragWithinEmailBody(structure).pipe(take(1));
|
|
2632
|
+
}
|
|
2633
|
+
openTemplateDialog(ev) {
|
|
2634
|
+
ev.stopImmediatePropagation();
|
|
2635
|
+
this.userInterfaceService.activeMatProgress$.next(true);
|
|
2636
|
+
const cachedTemplates = this.pbStorage.getCachedTemplateList();
|
|
2637
|
+
return iif(() => cachedTemplates.length > 0, deferOf(cachedTemplates), this.userRestApi.getAllUserTemplates$.pipe(tap(list => this.pbStorage.cacheTemplateList(list))))
|
|
2638
|
+
.pipe(
|
|
2639
|
+
// map(() => []),
|
|
2640
|
+
map(templates => {
|
|
2641
|
+
return [...templates, { category: 'latest', templates: this.pbStorage.getLatestUsedTemplates() }];
|
|
2642
|
+
}), finalize(() => this.userInterfaceService.activeMatProgress$.next(false)), exhaustMap(list => (list === null || list === void 0 ? void 0 : list.length) > 0 ? this.userInterfaceService.templatesListDialog$(list) : throwError(new Error('The template list is empty'))), switchMap(({ category, template }) => this.pbMiddlewaresService.chooseTemplate(category, template)), exhaustMap(({ category, template }) => this.userRestApi.getUserTemplateData$(category, template).pipe(tap(choosedTemplate => {
|
|
2643
|
+
if (category !== 'latest') {
|
|
2644
|
+
this.pbStorage.addTemplateToLatestUsed(choosedTemplate);
|
|
2645
|
+
}
|
|
2646
|
+
}))), catchError((error) => this.pbMiddlewaresService.catchError(error).pipe(switchMap(() => {
|
|
2647
|
+
this.userInterfaceService.notify(error.message);
|
|
2648
|
+
return EMPTY;
|
|
2649
|
+
}))), take(1))
|
|
2650
|
+
.subscribe((result) => {
|
|
2651
|
+
if (result) {
|
|
2652
|
+
this.emailObjectStore.setEmail(new PBEmail(result.templateData));
|
|
2653
|
+
}
|
|
2654
|
+
else {
|
|
2655
|
+
this.userInterfaceService.notify('No template was chosen');
|
|
2656
|
+
}
|
|
2657
|
+
});
|
|
2658
|
+
}
|
|
2659
|
+
dropNewStructure(event) {
|
|
2660
|
+
if (event.previousContainer === event.container) {
|
|
2661
|
+
this.emailObjectStore.changeStructureOrder(event);
|
|
2662
|
+
}
|
|
2663
|
+
else {
|
|
2664
|
+
this.pbMiddlewaresService
|
|
2665
|
+
.addStructure(event)
|
|
2666
|
+
.pipe(tap(newEvent => this.emailObjectStore.addStructure(newEvent)), take(1))
|
|
2667
|
+
.subscribe();
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
getStructureStyles(structure) {
|
|
2671
|
+
const { border, background, padding, margin, columnsWidth, fullWidth } = structure.options;
|
|
2672
|
+
return Object.assign(Object.assign(Object.assign({
|
|
2673
|
+
// direction,
|
|
2674
|
+
width: fullWidth ? '100%' : `${this.emailObjectStore.Email.general.width.value}px`, backgroundRepeat: background.repeat, backgroundColor: background.color, backgroundSize: createWidthHeight(background.size), backgroundPosition: 'top center', gridTemplateColumns: columnsWidth.map(fr => `${fr}fr`).join(' ') }, createBorder(border)), createPadding(padding)), createMargin(margin));
|
|
2675
|
+
}
|
|
2676
|
+
getBackgroundImage(structure) {
|
|
2677
|
+
const { background: { url } } = structure.options;
|
|
2678
|
+
return this.sanitizer.bypassSecurityTrustStyle(url && `url(${url})`);
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
BuilderContainerComponent.decorators = [
|
|
2682
|
+
{ type: Component, args: [{
|
|
2683
|
+
selector: 'pb-builder-container',
|
|
2684
|
+
template: "<!--\n ~ Copyright (c) 2024 Pobuca.\n ~ All rights reserved.\n -->\n\n<ng-container *ngIf=\"getStructures$ | async as structures\">\n <div cdkDropList [cdkDropListData]=\"structures\"\n cdkDropListConnectedTo=\"structures-drop-list\" id=\"structures-drop-list\"\n (cdkDropListDropped)=\"dropNewStructure($event)\" fxFlex fxLayout=\"column\" fxLayoutAlign=\"start center\">\n <pb-structure cdkDrag\n *ngFor=\"let structure of structures; trackBy: trackBy; let index = index\" [id]=\"structure.id\"\n [index]=\"index\" [ngClass]=\"structure.type\" [structure]=\"structure\"\n [ngStyle]=\"getStructureStyles(structure)\"\n [style.backgroundImage]=\"getBackgroundImage(structure)\"\n [cdkDragDisabled]=\"disableStructureDrag$(structure) | async\">\n <button cdkDragHandle mat-icon-button class=\"move\" *ngIf=\"!(disableStructureDrag$(structure) | async)\"\n i18n-matTooltip=\"structure|Move Structure\" matTooltip=\"Move Structure\">\n <mat-icon i18n-aria-label=\"structure|Move Structure\" aria-label=\"Move structure\" inline>pan_tool</mat-icon>\n </button>\n </pb-structure>\n\n <div class=\"empty-email mat-typography\" *ngIf=\"!structures.length\">\n <ng-content></ng-content>\n <div class=\"default-empty-email\" *ngIf=\"showTemplateList\">\n <h1 i18n=\"templates|First header\">Choose template from list</h1>\n <p i18n=\"templates|Description\">Or create one from scratch by dragging structures and blocks here!</p>\n <button type=\"button\" color=\"primary\" [disabled]=\"activeMatProgress$ | async\"\n (click)=\"openTemplateDialog($event)\" mat-flat-button i18n=\"templates|Choose a template button\">\n Choose template\n </button>\n </div>\n </div>\n </div>\n</ng-container>\n",
|
|
2685
|
+
providers: [
|
|
2686
|
+
// IpUserRestApiProvider
|
|
2687
|
+
],
|
|
2688
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
2689
|
+
// encapsulation: ViewEncapsulation.None
|
|
2690
|
+
,
|
|
2691
|
+
styles: ["/*!\n * Copyright (c) 2024 Pobuca.\n * All rights reserved.\n */pb-structure>.cdk-drag-handle{position:absolute;cursor:move;border-radius:50% 50% 0 0;height:30px;width:30px;font-size:smaller;line-height:0;top:-30px;right:0;background-color:#ff4081;color:#fff;opacity:0}pb-structure.cdk-drag-preview>.cdk-drag-handle,pb-structure:hover>.cdk-drag-handle{opacity:.8}pb-structure.cdk-drag-preview>.cdk-drag-handle:hover,pb-structure:hover>.cdk-drag-handle:hover{opacity:1}pb-structure ::ng-deep .cdk-drag-placeholder{background-color:#3f51b5;opacity:.6;cursor:move;border-radius:0}pb-structure ::ng-deep .cdk-drag-placeholder ::ng-deep :not(mat-icon){display:none}pb-structure ::ng-deep .cdk-drag-placeholder ::ng-deep mat-icon{padding:5px;color:#fff}:host{background-color:#f2f2f2;overflow-x:hidden;overflow-y:auto;position:relative;height:100%}:host #structures-drop-list{height:-moz-fit-content;height:fit-content;max-width:100%;padding-bottom:3rem;will-change:box-shadow}:host #structures-drop-list .empty-email{display:flex;justify-content:center;align-items:center;flex-direction:column;flex:1 1 100%;margin-top:10%}:host #structures-drop-list .empty-email .default-empty-email{text-align:center}:host #structures-drop-list.cdk-drop-list-dragging .empty-email{display:none}"]
|
|
2692
|
+
},] }
|
|
2693
|
+
];
|
|
2694
|
+
BuilderContainerComponent.ctorParameters = () => [
|
|
2695
|
+
{ type: PbUserInterfaceService },
|
|
2696
|
+
{ type: PbEmailObjectStoreService },
|
|
2697
|
+
{ type: PbUserRestApiService },
|
|
2698
|
+
{ type: PbStorageService },
|
|
2699
|
+
{ type: PbUserMiddlewaresService },
|
|
2700
|
+
{ type: DomSanitizer },
|
|
2701
|
+
{ type: undefined, decorators: [{ type: Inject, args: [PB_CONFIG,] }] }
|
|
2702
|
+
];
|
|
2703
|
+
BuilderContainerComponent.propDecorators = {
|
|
2704
|
+
onHostClick: [{ type: HostListener, args: ['click',] }]
|
|
2705
|
+
};
|
|
2706
|
+
|
|
2707
|
+
/*
|
|
2708
|
+
* Copyright (c) 2024 Pobuca.
|
|
2709
|
+
* All rights reserved.
|
|
2710
|
+
*/
|
|
2711
|
+
class FontStylesComponent {
|
|
2712
|
+
constructor(pbEmailObject, googleFonts, fallbackFonts) {
|
|
2713
|
+
this.pbEmailObject = pbEmailObject;
|
|
2714
|
+
this.fallbackFonts = fallbackFonts;
|
|
2715
|
+
this.fontsMap = new Map();
|
|
2716
|
+
this.stylesMap = new Map([
|
|
2717
|
+
['normal', $localize `:@@font_style_normal:Normal`],
|
|
2718
|
+
['italic', $localize `:@@font_style_italic:Italic`],
|
|
2719
|
+
['oblique', $localize `:@@font_style_oblique:Oblique`]
|
|
2720
|
+
]);
|
|
2721
|
+
googleFonts.forEach(font => {
|
|
2722
|
+
const [family, ...weights] = font.match(/[^\d:,]{2,}|\d{3}/g);
|
|
2723
|
+
this.fontsMap.set(family.replace('+', ' '), [...new Set(weights.map(Number))]);
|
|
2724
|
+
});
|
|
2725
|
+
}
|
|
2726
|
+
get fontWeightList() {
|
|
2727
|
+
return this.isGoogleFont() ? this.fontsMap.get(this.font.family) : [100, 400, 500, 700, 900];
|
|
2728
|
+
}
|
|
2729
|
+
get fontFamilyList() {
|
|
2730
|
+
return [...this.fontsMap.keys(), ...this.fallbackFonts];
|
|
2731
|
+
}
|
|
2732
|
+
get styles() {
|
|
2733
|
+
return [...this.stylesMap.keys()];
|
|
2734
|
+
}
|
|
2735
|
+
markForCheck() {
|
|
2736
|
+
this.pbEmailObject.markForCheck();
|
|
2737
|
+
}
|
|
2738
|
+
isGoogleFont() {
|
|
2739
|
+
return this.fontsMap.has(this.font.family);
|
|
2740
|
+
}
|
|
2741
|
+
getStyleLabel(style) {
|
|
2742
|
+
// tslint:disable-next-line:no-non-null-assertion
|
|
2743
|
+
return this.stylesMap.get(style);
|
|
2744
|
+
}
|
|
2745
|
+
hasProperty(property) {
|
|
2746
|
+
return this.font.hasOwnProperty(property);
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
FontStylesComponent.decorators = [
|
|
2750
|
+
{ type: Component, args: [{
|
|
2751
|
+
selector: 'pb-font-styles',
|
|
2752
|
+
template: `
|
|
2753
|
+
<div class="group two">
|
|
2754
|
+
<mat-form-field appearance="outline" *ngIf="hasProperty('family')">
|
|
2755
|
+
<mat-label i18n="settings|Font Family">Family</mat-label>
|
|
2756
|
+
<mat-select [(value)]="font.family" disableRipple (selectionChange)="markForCheck()">
|
|
2757
|
+
<mat-option *ngFor="let name of fontFamilyList" [ngStyle]="{ fontFamily: name }" [value]="name">
|
|
2758
|
+
{{ name }}
|
|
2759
|
+
</mat-option>
|
|
2760
|
+
</mat-select>
|
|
2761
|
+
<mat-hint *ngIf="isGoogleFont()" i18n="settings|Partial support">Partial support.</mat-hint>
|
|
2762
|
+
<mat-hint *ngIf="!isGoogleFont()" i18n="settings|Full support">Full support.</mat-hint>
|
|
2763
|
+
</mat-form-field>
|
|
2764
|
+
<mat-form-field appearance="outline" *ngIf="hasProperty('fallback')">
|
|
2765
|
+
<mat-label i18n="settings|Font Fallback">Fallback</mat-label>
|
|
2766
|
+
<mat-select [(value)]="font.fallback" [disabled]="!isGoogleFont()" disableRipple (selectionChange)="markForCheck()">
|
|
2767
|
+
<mat-option *ngFor="let name of fallbackFonts" [ngStyle]="{ fontFamily: name }" [value]="name">
|
|
2768
|
+
{{ name }}
|
|
2769
|
+
</mat-option>
|
|
2770
|
+
</mat-select>
|
|
2771
|
+
<mat-hint i18n="settings|Full support">Full support.</mat-hint>
|
|
2772
|
+
</mat-form-field>
|
|
2773
|
+
</div>
|
|
2774
|
+
|
|
2775
|
+
<div class="group three">
|
|
2776
|
+
<mat-form-field appearance="outline" *ngIf="hasProperty('size')">
|
|
2777
|
+
<mat-label i18n="settings|Font Size">Size</mat-label>
|
|
2778
|
+
<input matInput type="number" max="30" min="10" step="1" (input)="markForCheck()" [(ngModel)]="font.size" />
|
|
2779
|
+
</mat-form-field>
|
|
2780
|
+
<mat-form-field appearance="outline" *ngIf="hasProperty('weight')">
|
|
2781
|
+
<mat-label i18n="settings|Font Weight">Weight</mat-label>
|
|
2782
|
+
<mat-select placeholder="Weight" [(value)]="font.weight" disableRipple (selectionChange)="markForCheck()">
|
|
2783
|
+
<mat-option *ngFor="let weight of fontWeightList" [value]="weight">
|
|
2784
|
+
{{ weight }}
|
|
2785
|
+
</mat-option>
|
|
2786
|
+
</mat-select>
|
|
2787
|
+
</mat-form-field>
|
|
2788
|
+
<mat-form-field appearance="outline" style="min-width: 90px;">
|
|
2789
|
+
<mat-label i18n="settings|Font Style">Style</mat-label>
|
|
2790
|
+
<mat-select placeholder="Style" [(value)]="font.style" disableRipple (selectionChange)="markForCheck()">
|
|
2791
|
+
<mat-option *ngFor="let style of styles" [value]="style" i18n>
|
|
2792
|
+
{{ getStyleLabel(style) }}
|
|
2793
|
+
</mat-option>
|
|
2794
|
+
</mat-select>
|
|
2795
|
+
</mat-form-field>
|
|
2796
|
+
</div>
|
|
2797
|
+
`,
|
|
2798
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2799
|
+
styles: [`
|
|
2800
|
+
:host {
|
|
2801
|
+
display: block;
|
|
2802
|
+
width: 100%;
|
|
2803
|
+
}
|
|
2804
|
+
`]
|
|
2805
|
+
},] }
|
|
2806
|
+
];
|
|
2807
|
+
FontStylesComponent.ctorParameters = () => [
|
|
2808
|
+
{ type: PbEmailObjectStoreService },
|
|
2809
|
+
{ type: Array, decorators: [{ type: Inject, args: [PB_GOOGLE_FONTS,] }] },
|
|
2810
|
+
{ type: Array, decorators: [{ type: Inject, args: [PB_FALLBACK_FONTS,] }] }
|
|
2811
|
+
];
|
|
2812
|
+
FontStylesComponent.propDecorators = {
|
|
2813
|
+
font: [{ type: Input }]
|
|
2814
|
+
};
|
|
2815
|
+
|
|
2816
|
+
/*
|
|
2817
|
+
* Copyright (c) 2024 Pobuca.
|
|
2818
|
+
* All rights reserved.
|
|
2819
|
+
*/
|
|
2820
|
+
class PaddingComponent {
|
|
2821
|
+
constructor(emailObject) {
|
|
2822
|
+
this.emailObject = emailObject;
|
|
2823
|
+
}
|
|
2824
|
+
markForCheck() {
|
|
2825
|
+
this.emailObject.markForCheck();
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
PaddingComponent.decorators = [
|
|
2829
|
+
{ type: Component, args: [{
|
|
2830
|
+
selector: 'pb-padding',
|
|
2831
|
+
template: `
|
|
2832
|
+
<div class="group four">
|
|
2833
|
+
<mat-form-field appearance="outline">
|
|
2834
|
+
<mat-label i18n="Padding Settings|Padding Top">Top</mat-label>
|
|
2835
|
+
<input matInput type="number" min="0" step="1" (input)="markForCheck()" [(ngModel)]="padding.top" />
|
|
2836
|
+
</mat-form-field>
|
|
2837
|
+
<mat-form-field appearance="outline">
|
|
2838
|
+
<mat-label i18n="Padding Settings|Padding Right">Right</mat-label>
|
|
2839
|
+
<input matInput type="number" min="0" step="1" (input)="markForCheck()" [(ngModel)]="padding.right" />
|
|
2840
|
+
</mat-form-field>
|
|
2841
|
+
<mat-form-field appearance="outline">
|
|
2842
|
+
<mat-label i18n="Padding Settings|Padding Bottom">Bottom</mat-label>
|
|
2843
|
+
<input matInput type="number" min="0" step="1" (input)="markForCheck()" [(ngModel)]="padding.bottom" />
|
|
2844
|
+
</mat-form-field>
|
|
2845
|
+
<mat-form-field appearance="outline">
|
|
2846
|
+
<mat-label i18n="Padding Settings|Padding Left">Left</mat-label>
|
|
2847
|
+
<input matInput type="number" min="0" step="1" (input)="markForCheck()" [(ngModel)]="padding.left" />
|
|
2848
|
+
</mat-form-field>
|
|
2849
|
+
</div>
|
|
2850
|
+
`,
|
|
2851
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2852
|
+
styles: [`
|
|
2853
|
+
:host {
|
|
2854
|
+
display: block;
|
|
2855
|
+
width: 100%;
|
|
2856
|
+
}
|
|
2857
|
+
`]
|
|
2858
|
+
},] }
|
|
2859
|
+
];
|
|
2860
|
+
PaddingComponent.ctorParameters = () => [
|
|
2861
|
+
{ type: PbEmailObjectStoreService }
|
|
2862
|
+
];
|
|
2863
|
+
PaddingComponent.propDecorators = {
|
|
2864
|
+
padding: [{ type: Input }]
|
|
2865
|
+
};
|
|
2866
|
+
|
|
2867
|
+
/*
|
|
2868
|
+
* Copyright (c) 2024 Pobuca.
|
|
2869
|
+
* All rights reserved.
|
|
2870
|
+
*/
|
|
2871
|
+
class LineHeightComponent {
|
|
2872
|
+
constructor(emailObject) {
|
|
2873
|
+
this.emailObject = emailObject;
|
|
2874
|
+
this.label = $localize `:@@line_height_label:Line Height`;
|
|
2875
|
+
this.units = ['%', 'px', 'none'];
|
|
2876
|
+
this.unitsLabels = new Map([
|
|
2877
|
+
['%', $localize `:@@unit_percent:Percent`],
|
|
2878
|
+
['px', $localize `:@@unit_pixels:Pixels`],
|
|
2879
|
+
['none', $localize `:@@unit_none:None`]
|
|
2880
|
+
]);
|
|
2881
|
+
}
|
|
2882
|
+
markForCheck() {
|
|
2883
|
+
this.emailObject.markForCheck();
|
|
2884
|
+
}
|
|
2885
|
+
getUnitLabel(unit) {
|
|
2886
|
+
return this.unitsLabels.get(unit);
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
LineHeightComponent.decorators = [
|
|
2890
|
+
{ type: Component, args: [{
|
|
2891
|
+
selector: 'pb-line-height',
|
|
2892
|
+
template: `
|
|
2893
|
+
<div class="group">
|
|
2894
|
+
<mat-form-field appearance="outline">
|
|
2895
|
+
<mat-label>{{ label }}</mat-label>
|
|
2896
|
+
<input
|
|
2897
|
+
matInput
|
|
2898
|
+
[(ngModel)]="lineHeight.value"
|
|
2899
|
+
type="number"
|
|
2900
|
+
step="1"
|
|
2901
|
+
(input)="markForCheck()"
|
|
2902
|
+
[disabled]="lineHeight.unit === 'none'"
|
|
2903
|
+
/>
|
|
2904
|
+
</mat-form-field>
|
|
2905
|
+
<mat-form-field appearance="outline">
|
|
2906
|
+
<mat-label i18n="Line Height Settings|Change Line Height Unit">Unit</mat-label>
|
|
2907
|
+
<mat-select placeholder="Unit" [(value)]="lineHeight.unit" (selectionChange)="markForCheck()" disableRipple>
|
|
2908
|
+
<mat-option *ngFor="let unit of units" [value]="unit" i18n>
|
|
2909
|
+
{{ getUnitLabel(unit) }}
|
|
2910
|
+
</mat-option>
|
|
2911
|
+
</mat-select>
|
|
2912
|
+
</mat-form-field>
|
|
2913
|
+
</div>
|
|
2914
|
+
`,
|
|
2915
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2916
|
+
styles: [`
|
|
2917
|
+
:host {
|
|
2918
|
+
display: block;
|
|
2919
|
+
width: 100%;
|
|
2920
|
+
}
|
|
2921
|
+
`]
|
|
2922
|
+
},] }
|
|
2923
|
+
];
|
|
2924
|
+
LineHeightComponent.ctorParameters = () => [
|
|
2925
|
+
{ type: PbEmailObjectStoreService }
|
|
2926
|
+
];
|
|
2927
|
+
LineHeightComponent.propDecorators = {
|
|
2928
|
+
lineHeight: [{ type: Input }],
|
|
2929
|
+
label: [{ type: Input }],
|
|
2930
|
+
units: [{ type: Input }]
|
|
2931
|
+
};
|
|
2932
|
+
|
|
2933
|
+
/*
|
|
2934
|
+
* Copyright (c) 2024 Pobuca.
|
|
2935
|
+
* All rights reserved.
|
|
2936
|
+
*/
|
|
2937
|
+
class WidthHeightComponent {
|
|
2938
|
+
constructor(emailObject) {
|
|
2939
|
+
this.emailObject = emailObject;
|
|
2940
|
+
this.disabled = false;
|
|
2941
|
+
this.units = new Map([
|
|
2942
|
+
['%', $localize `:@@unit_percent:Percent`],
|
|
2943
|
+
['px', $localize `:@@unit_pixels:Pixels`],
|
|
2944
|
+
['contain', $localize `:@@unit_contain:Contain`],
|
|
2945
|
+
['cover', $localize `:@@unit_cover:Cover`]
|
|
2946
|
+
]);
|
|
2947
|
+
}
|
|
2948
|
+
markForCheck() {
|
|
2949
|
+
this.emailObject.markForCheck();
|
|
2950
|
+
}
|
|
2951
|
+
getLabel() {
|
|
2952
|
+
return this.label;
|
|
2953
|
+
}
|
|
2954
|
+
toggleChange({ checked }) {
|
|
2955
|
+
this.model.auto = checked;
|
|
2956
|
+
this.markForCheck();
|
|
2957
|
+
}
|
|
2958
|
+
getUnits() {
|
|
2959
|
+
return this.model.units || ['%', 'px'];
|
|
2960
|
+
}
|
|
2961
|
+
disableValueField() {
|
|
2962
|
+
return this.model.auto || ['%', 'px'].indexOf(this.model.unit) === -1;
|
|
2963
|
+
}
|
|
2964
|
+
showAutoSlider() {
|
|
2965
|
+
return this.model.hasOwnProperty('auto');
|
|
2966
|
+
}
|
|
2967
|
+
getUnitLabel(unit) {
|
|
2968
|
+
return this.units.get(unit);
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
WidthHeightComponent.decorators = [
|
|
2972
|
+
{ type: Component, args: [{
|
|
2973
|
+
selector: 'pb-width-height',
|
|
2974
|
+
template: `
|
|
2975
|
+
<div class="group" [ngClass]="{ three: showAutoSlider() }">
|
|
2976
|
+
<mat-form-field appearance="outline">
|
|
2977
|
+
<mat-label>{{ getLabel() }}</mat-label>
|
|
2978
|
+
<input
|
|
2979
|
+
matInput
|
|
2980
|
+
[(ngModel)]="model.value"
|
|
2981
|
+
[disabled]="disableValueField() || disabled"
|
|
2982
|
+
type="number"
|
|
2983
|
+
[placeholder]="getLabel()"
|
|
2984
|
+
(input)="markForCheck()"
|
|
2985
|
+
/>
|
|
2986
|
+
</mat-form-field>
|
|
2987
|
+
<mat-form-field appearance="outline">
|
|
2988
|
+
<mat-label i18n="Size Settings|Size Unit">Unit</mat-label>
|
|
2989
|
+
<mat-select [disabled]="model.auto || disabled" [(value)]="model.unit" (selectionChange)="markForCheck()" disableRipple>
|
|
2990
|
+
<mat-option *ngFor="let unit of getUnits()" [value]="unit" i18n>
|
|
2991
|
+
{{ getUnitLabel(unit) }}
|
|
2992
|
+
</mat-option>
|
|
2993
|
+
</mat-select>
|
|
2994
|
+
</mat-form-field>
|
|
2995
|
+
<mat-slide-toggle
|
|
2996
|
+
style="margin-top: -20px;"
|
|
2997
|
+
*ngIf="showAutoSlider()"
|
|
2998
|
+
[checked]="model.auto"
|
|
2999
|
+
(change)="toggleChange($event)"
|
|
3000
|
+
[disabled]="disabled"
|
|
3001
|
+
i18n="Size Settings|Size Auto"
|
|
3002
|
+
>
|
|
3003
|
+
Auto
|
|
3004
|
+
</mat-slide-toggle>
|
|
3005
|
+
</div>
|
|
3006
|
+
`,
|
|
3007
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3008
|
+
styles: [`
|
|
3009
|
+
:host {
|
|
3010
|
+
display: block;
|
|
3011
|
+
width: 100%;
|
|
3012
|
+
}
|
|
3013
|
+
`]
|
|
3014
|
+
},] }
|
|
3015
|
+
];
|
|
3016
|
+
WidthHeightComponent.ctorParameters = () => [
|
|
3017
|
+
{ type: PbEmailObjectStoreService }
|
|
3018
|
+
];
|
|
3019
|
+
WidthHeightComponent.propDecorators = {
|
|
3020
|
+
model: [{ type: Input }],
|
|
3021
|
+
label: [{ type: Input }],
|
|
3022
|
+
disabled: [{ type: Input }]
|
|
3023
|
+
};
|
|
3024
|
+
|
|
3025
|
+
/*
|
|
3026
|
+
* Copyright (c) 2024 Pobuca.
|
|
3027
|
+
* All rights reserved.
|
|
3028
|
+
*/
|
|
3029
|
+
class BorderComponent {
|
|
3030
|
+
constructor(emailObject) {
|
|
3031
|
+
this.emailObject = emailObject;
|
|
3032
|
+
this._styleLabels = new Map([
|
|
3033
|
+
['solid', $localize `:@@border_style_solid:Solid`],
|
|
3034
|
+
['dotted', $localize `:@@border_style_dotted:Dotted`],
|
|
3035
|
+
['dashed', $localize `:@@border_style_dashed:Dashed`],
|
|
3036
|
+
['double', $localize `:@@border_style_double:Double`],
|
|
3037
|
+
['groove', $localize `:@@border_style_groove:Groove`]
|
|
3038
|
+
]);
|
|
3039
|
+
}
|
|
3040
|
+
get styleLabels() {
|
|
3041
|
+
return [...this._styleLabels.keys()];
|
|
3042
|
+
}
|
|
3043
|
+
getStyleLabel(style) {
|
|
3044
|
+
// tslint:disable-next-line:no-non-null-assertion
|
|
3045
|
+
return this._styleLabels.get(style);
|
|
3046
|
+
}
|
|
3047
|
+
markForCheck() {
|
|
3048
|
+
this.emailObject.markForCheck();
|
|
3049
|
+
}
|
|
3050
|
+
isEven() {
|
|
3051
|
+
return Object.keys(this.border).length % 2 === 0;
|
|
3052
|
+
}
|
|
3053
|
+
hasOwnProperty(property) {
|
|
3054
|
+
return this.border.hasOwnProperty(property);
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
BorderComponent.decorators = [
|
|
3058
|
+
{ type: Component, args: [{
|
|
3059
|
+
selector: 'pb-border',
|
|
3060
|
+
template: `
|
|
3061
|
+
<div class="group" [class.three]="isEven()">
|
|
3062
|
+
<mat-form-field appearance="outline" *ngIf="hasOwnProperty('width')">
|
|
3063
|
+
<mat-label i18n="Border Settings|Change Border Width">Width</mat-label>
|
|
3064
|
+
<input
|
|
3065
|
+
matInput
|
|
3066
|
+
[(ngModel)]="border.width"
|
|
3067
|
+
type="number"
|
|
3068
|
+
min="0"
|
|
3069
|
+
step="1"
|
|
3070
|
+
(input)="markForCheck()"
|
|
3071
|
+
i18n-placeholder
|
|
3072
|
+
placeholder="Border Settings|Change Border Width"
|
|
3073
|
+
/>
|
|
3074
|
+
</mat-form-field>
|
|
3075
|
+
<mat-form-field appearance="outline" *ngIf="hasOwnProperty('radius')">
|
|
3076
|
+
<mat-label i18n="Border Settings|Change Border Radius">Radius</mat-label>
|
|
3077
|
+
<input
|
|
3078
|
+
matInput
|
|
3079
|
+
[(ngModel)]="border.radius"
|
|
3080
|
+
type="number"
|
|
3081
|
+
min="0"
|
|
3082
|
+
step="1"
|
|
3083
|
+
(input)="markForCheck()"
|
|
3084
|
+
i18n-placeholder
|
|
3085
|
+
placeholder="Border Settings|Change Border Radius"
|
|
3086
|
+
/>
|
|
3087
|
+
</mat-form-field>
|
|
3088
|
+
<mat-form-field appearance="outline" *ngIf="hasOwnProperty('style')" style="min-width: 90px">
|
|
3089
|
+
<mat-label i18n="Border Settings|Change Border Style">Style</mat-label>
|
|
3090
|
+
<mat-select
|
|
3091
|
+
placeholder="Style"
|
|
3092
|
+
[disabled]="!border.width"
|
|
3093
|
+
[(value)]="border.style"
|
|
3094
|
+
(selectionChange)="markForCheck()"
|
|
3095
|
+
disableRipple
|
|
3096
|
+
>
|
|
3097
|
+
<mat-option *ngFor="let style of styleLabels" [value]="style">
|
|
3098
|
+
{{ getStyleLabel(style) }}
|
|
3099
|
+
</mat-option>
|
|
3100
|
+
</mat-select>
|
|
3101
|
+
</mat-form-field>
|
|
3102
|
+
</div>
|
|
3103
|
+
<pb-color [options]="border" [disabled]="!border.width" *ngIf="hasOwnProperty('color')" key="color"></pb-color>
|
|
3104
|
+
`,
|
|
3105
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3106
|
+
styles: [`
|
|
3107
|
+
:host {
|
|
3108
|
+
display: block;
|
|
3109
|
+
width: 100%;
|
|
3110
|
+
}
|
|
3111
|
+
|
|
3112
|
+
mat-form-field {
|
|
3113
|
+
width: 100%;
|
|
3114
|
+
}
|
|
3115
|
+
`]
|
|
3116
|
+
},] }
|
|
3117
|
+
];
|
|
3118
|
+
BorderComponent.ctorParameters = () => [
|
|
3119
|
+
{ type: PbEmailObjectStoreService }
|
|
3120
|
+
];
|
|
3121
|
+
BorderComponent.propDecorators = {
|
|
3122
|
+
border: [{ type: Input }]
|
|
3123
|
+
};
|
|
3124
|
+
|
|
3125
|
+
/*
|
|
3126
|
+
* Copyright (c) 2024 Pobuca.
|
|
3127
|
+
* All rights reserved.
|
|
3128
|
+
*/
|
|
3129
|
+
// import { PbEmailBuilderService } from '../pb-email-builder.service';
|
|
3130
|
+
// import { parseColor } from '../utils';
|
|
3131
|
+
class ColorComponent {
|
|
3132
|
+
constructor(emailObject) {
|
|
3133
|
+
this.emailObject = emailObject;
|
|
3134
|
+
this.disabled = false;
|
|
3135
|
+
this.key = 'backgroundColor';
|
|
3136
|
+
this.transparent = false;
|
|
3137
|
+
this.showColorPicker = false;
|
|
3138
|
+
}
|
|
3139
|
+
get currentColorModel() {
|
|
3140
|
+
return this.options[this.key];
|
|
3141
|
+
}
|
|
3142
|
+
set currentColorModel(hex) {
|
|
3143
|
+
this.options[this.key] = hex;
|
|
3144
|
+
}
|
|
3145
|
+
get currentColor() {
|
|
3146
|
+
return this.transparent ? this.oldValue : this.currentColorModel;
|
|
3147
|
+
}
|
|
3148
|
+
set currentColor(hex) {
|
|
3149
|
+
this.currentColorModel = hex;
|
|
3150
|
+
}
|
|
3151
|
+
onHostMouseLeave() {
|
|
3152
|
+
this.showColorPicker = false;
|
|
3153
|
+
}
|
|
3154
|
+
changeColor(event) {
|
|
3155
|
+
this.currentColor = event.color.hex;
|
|
3156
|
+
this.emailObject.markForCheck();
|
|
3157
|
+
}
|
|
3158
|
+
makeTransparentColor() {
|
|
3159
|
+
this.showColorPicker = false;
|
|
3160
|
+
if (this.transparent) {
|
|
3161
|
+
this.oldValue = this.currentColorModel;
|
|
3162
|
+
this.currentColor = 'transparent';
|
|
3163
|
+
}
|
|
3164
|
+
else {
|
|
3165
|
+
this.currentColor = this.oldValue;
|
|
3166
|
+
}
|
|
3167
|
+
this.emailObject.markForCheck();
|
|
3168
|
+
}
|
|
3169
|
+
ngOnInit() {
|
|
3170
|
+
if (this.currentColor === 'transparent') {
|
|
3171
|
+
this.transparent = true;
|
|
3172
|
+
this.oldValue = '#ffffff';
|
|
3173
|
+
}
|
|
3174
|
+
// this.changeContrastColor();
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
ColorComponent.decorators = [
|
|
3178
|
+
{ type: Component, args: [{
|
|
3179
|
+
selector: 'pb-color',
|
|
3180
|
+
template: `
|
|
3181
|
+
<div class="group" *ngIf="showTransparent">
|
|
3182
|
+
<mat-slide-toggle
|
|
3183
|
+
[disabled]="disabled"
|
|
3184
|
+
[(ngModel)]="transparent"
|
|
3185
|
+
(change)="makeTransparentColor()"
|
|
3186
|
+
i18n="Color settings|Make color transparent"
|
|
3187
|
+
>
|
|
3188
|
+
Transparent
|
|
3189
|
+
</mat-slide-toggle>
|
|
3190
|
+
</div>
|
|
3191
|
+
<div class="group two">
|
|
3192
|
+
<mat-form-field appearance="outline">
|
|
3193
|
+
<mat-label i18n="Color settings|Change color">Color</mat-label>
|
|
3194
|
+
<input matInput [disabled]="transparent || disabled" (focus)="showColorPicker = false" [(ngModel)]="currentColor" type="text" />
|
|
3195
|
+
</mat-form-field>
|
|
3196
|
+
<div class="choose-color" [class.transparent]="transparent || disabled">
|
|
3197
|
+
<div class="current-color" (mouseover)="showColorPicker = !transparent" [style.backgroundColor]="currentColor"></div>
|
|
3198
|
+
<color-chrome
|
|
3199
|
+
[disableAlpha]="true"
|
|
3200
|
+
*ngIf="showColorPicker"
|
|
3201
|
+
(onChangeComplete)="changeColor($event)"
|
|
3202
|
+
[color]="currentColor"
|
|
3203
|
+
></color-chrome>
|
|
3204
|
+
</div>
|
|
3205
|
+
</div>
|
|
3206
|
+
`,
|
|
3207
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3208
|
+
styles: [`
|
|
3209
|
+
:host {
|
|
3210
|
+
display: block;
|
|
3211
|
+
position: relative;
|
|
3212
|
+
}
|
|
3213
|
+
|
|
3214
|
+
mat-form-field {
|
|
3215
|
+
width: 100%;
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
.choose-color {
|
|
3219
|
+
display: block;
|
|
3220
|
+
height: 100%;
|
|
3221
|
+
width: 100%;
|
|
3222
|
+
}
|
|
3223
|
+
|
|
3224
|
+
.current-color {
|
|
3225
|
+
width: 100%;
|
|
3226
|
+
height: 48px;
|
|
3227
|
+
border-radius: 5px;
|
|
3228
|
+
margin: 0.25em 0;
|
|
3229
|
+
cursor: pointer;
|
|
3230
|
+
display: flex;
|
|
3231
|
+
align-items: center;
|
|
3232
|
+
justify-content: center;
|
|
3233
|
+
border: 1px solid rgba(0, 0, 0, 0.12);
|
|
3234
|
+
transition: background-color 500ms cubic-bezier(0.445, 0.05, 0.55, 0.95);
|
|
3235
|
+
}
|
|
3236
|
+
|
|
3237
|
+
.choose-color.transparent .current-color {
|
|
3238
|
+
opacity: 0.5;
|
|
3239
|
+
pointer-events: none;
|
|
3240
|
+
}
|
|
3241
|
+
|
|
3242
|
+
color-chrome {
|
|
3243
|
+
position: absolute;
|
|
3244
|
+
top: calc(100% - 1em);
|
|
3245
|
+
right: 0;
|
|
3246
|
+
z-index: 9;
|
|
3247
|
+
}
|
|
3248
|
+
`]
|
|
3249
|
+
},] }
|
|
3250
|
+
];
|
|
3251
|
+
ColorComponent.ctorParameters = () => [
|
|
3252
|
+
{ type: PbEmailObjectStoreService }
|
|
3253
|
+
];
|
|
3254
|
+
ColorComponent.propDecorators = {
|
|
3255
|
+
options: [{ type: Input }],
|
|
3256
|
+
showTransparent: [{ type: Input }],
|
|
3257
|
+
disabled: [{ type: Input }],
|
|
3258
|
+
key: [{ type: Input }],
|
|
3259
|
+
onHostMouseLeave: [{ type: HostListener, args: ['mouseleave',] }]
|
|
3260
|
+
};
|
|
3261
|
+
|
|
3262
|
+
/*
|
|
3263
|
+
* Copyright (c) 2024 Pobuca.
|
|
3264
|
+
* All rights reserved.
|
|
3265
|
+
*/
|
|
3266
|
+
class LinkComponent {
|
|
3267
|
+
constructor(emailObject) {
|
|
3268
|
+
this.emailObject = emailObject;
|
|
3269
|
+
this.targetLabels = new Map([
|
|
3270
|
+
['_blank', $localize `:@@link_target_blank:Blank`],
|
|
3271
|
+
['_self', $localize `:@@link_target_self:Self`],
|
|
3272
|
+
['_parent', $localize `:@@link_target_parent:Parent`],
|
|
3273
|
+
['_top', $localize `:@@link_target_top:Top`]
|
|
3274
|
+
]);
|
|
3275
|
+
}
|
|
3276
|
+
markForCheck() {
|
|
3277
|
+
this.emailObject.markForCheck();
|
|
3278
|
+
}
|
|
3279
|
+
getTargets() {
|
|
3280
|
+
return ['_blank', '_self', '_parent', '_top'];
|
|
3281
|
+
}
|
|
3282
|
+
getTargetLabel(target) {
|
|
3283
|
+
return this.targetLabels.get(target);
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
LinkComponent.decorators = [
|
|
3287
|
+
{ type: Component, args: [{
|
|
3288
|
+
selector: 'pb-link',
|
|
3289
|
+
template: `
|
|
3290
|
+
<div class="group f-large">
|
|
3291
|
+
<mat-form-field appearance="outline">
|
|
3292
|
+
<mat-label i18n="Link Settings|Link">Link</mat-label>
|
|
3293
|
+
<input matInput [(ngModel)]="link.href" type="url" placeholder="Link" (blur)="markForCheck()" />
|
|
3294
|
+
</mat-form-field>
|
|
3295
|
+
<mat-form-field appearance="outline">
|
|
3296
|
+
<mat-label i18n="Link Settings|Target">Target</mat-label>
|
|
3297
|
+
<mat-select
|
|
3298
|
+
placeholder="Target"
|
|
3299
|
+
i18n-placeholder="Link Settings|Target"
|
|
3300
|
+
[(value)]="link.target"
|
|
3301
|
+
disableRipple
|
|
3302
|
+
(selectionChange)="markForCheck()"
|
|
3303
|
+
>
|
|
3304
|
+
<mat-option *ngFor="let target of getTargets()" [value]="target">
|
|
3305
|
+
{{ getTargetLabel(target) }}
|
|
3306
|
+
</mat-option>
|
|
3307
|
+
</mat-select>
|
|
3308
|
+
</mat-form-field>
|
|
3309
|
+
</div>
|
|
3310
|
+
`,
|
|
3311
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3312
|
+
styles: [`
|
|
3313
|
+
:host {
|
|
3314
|
+
display: block;
|
|
3315
|
+
width: 100%;
|
|
3316
|
+
}
|
|
3317
|
+
`]
|
|
3318
|
+
},] }
|
|
3319
|
+
];
|
|
3320
|
+
LinkComponent.ctorParameters = () => [
|
|
3321
|
+
{ type: PbEmailObjectStoreService }
|
|
3322
|
+
];
|
|
3323
|
+
LinkComponent.propDecorators = {
|
|
3324
|
+
link: [{ type: Input }]
|
|
3325
|
+
};
|
|
3326
|
+
|
|
3327
|
+
/*
|
|
3328
|
+
* Copyright (c) 2024 Pobuca.
|
|
3329
|
+
* All rights reserved.
|
|
3330
|
+
*/
|
|
3331
|
+
class AlignComponent {
|
|
3332
|
+
constructor(emailObject) {
|
|
3333
|
+
this.emailObject = emailObject;
|
|
3334
|
+
this.mode = 'horizontal';
|
|
3335
|
+
this.labels = new Map([
|
|
3336
|
+
['left', $localize `:@@horizontal_align_left:Left`],
|
|
3337
|
+
['center', $localize `:@@horizontal_align_center:Center`],
|
|
3338
|
+
['right', $localize `:@@horizontal_align_right:Right`],
|
|
3339
|
+
['top', $localize `:@@vertical_align_top:Top`],
|
|
3340
|
+
['middle', $localize `:@@vertical_align_middle:Middle`],
|
|
3341
|
+
['bottom', $localize `:@@vertical_align_bottom:Bottom`]
|
|
3342
|
+
]);
|
|
3343
|
+
}
|
|
3344
|
+
get currentModel() {
|
|
3345
|
+
return this.model.align || this.model.verticalAlign;
|
|
3346
|
+
}
|
|
3347
|
+
set currentModel(value) {
|
|
3348
|
+
if (this.model.align) {
|
|
3349
|
+
this.model.align = value;
|
|
3350
|
+
}
|
|
3351
|
+
else {
|
|
3352
|
+
this.model.verticalAlign = value;
|
|
3353
|
+
}
|
|
3354
|
+
this.emailObject.markForCheck();
|
|
3355
|
+
}
|
|
3356
|
+
getLabel(key) {
|
|
3357
|
+
return this.labels.get(key);
|
|
3358
|
+
}
|
|
3359
|
+
getPositions() {
|
|
3360
|
+
if (this.mode === 'vertical') {
|
|
3361
|
+
return ['top', 'middle', 'bottom'];
|
|
3362
|
+
}
|
|
3363
|
+
return ['left', 'center', 'right'];
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
AlignComponent.decorators = [
|
|
3367
|
+
{ type: Component, args: [{
|
|
3368
|
+
selector: 'pb-align',
|
|
3369
|
+
template: `
|
|
3370
|
+
<mat-form-field appearance="outline">
|
|
3371
|
+
<mat-label [ngSwitch]="mode">
|
|
3372
|
+
<ng-container *ngSwitchCase="'vertical'" i18n="Align Settings|Vertical Align">Vertical Align</ng-container>
|
|
3373
|
+
<ng-container *ngSwitchDefault i18n="Align Settings|Align">Align</ng-container>
|
|
3374
|
+
</mat-label>
|
|
3375
|
+
<mat-select [(ngModel)]="currentModel" disableRipple [disabled]="disabled">
|
|
3376
|
+
<mat-option *ngFor="let position of getPositions()" [value]="position">
|
|
3377
|
+
{{ getLabel(position) }}
|
|
3378
|
+
</mat-option>
|
|
3379
|
+
</mat-select>
|
|
3380
|
+
</mat-form-field>
|
|
3381
|
+
`,
|
|
3382
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3383
|
+
styles: [`
|
|
3384
|
+
:host {
|
|
3385
|
+
display: block;
|
|
3386
|
+
width: 100%;
|
|
3387
|
+
}
|
|
3388
|
+
|
|
3389
|
+
mat-form-field {
|
|
3390
|
+
width: 100%;
|
|
3391
|
+
}
|
|
3392
|
+
`]
|
|
3393
|
+
},] }
|
|
3394
|
+
];
|
|
3395
|
+
AlignComponent.ctorParameters = () => [
|
|
3396
|
+
{ type: PbEmailObjectStoreService }
|
|
3397
|
+
];
|
|
3398
|
+
AlignComponent.propDecorators = {
|
|
3399
|
+
model: [{ type: Input }],
|
|
3400
|
+
mode: [{ type: Input }],
|
|
3401
|
+
disabled: [{ type: Input }]
|
|
3402
|
+
};
|
|
3403
|
+
|
|
3404
|
+
/*
|
|
3405
|
+
* Copyright (c) 2024 Pobuca.
|
|
3406
|
+
* All rights reserved.
|
|
3407
|
+
*/
|
|
3408
|
+
class DirectionComponent {
|
|
3409
|
+
constructor(emailObject) {
|
|
3410
|
+
this.emailObject = emailObject;
|
|
3411
|
+
this.dirLabels = new Map([
|
|
3412
|
+
['ltr', $localize `:@@direction_ltr:Left to right`],
|
|
3413
|
+
['rtl', $localize `:@@direction_rtl:Right to left`]
|
|
3414
|
+
]);
|
|
3415
|
+
}
|
|
3416
|
+
markForCheck() {
|
|
3417
|
+
this.emailObject.markForCheck();
|
|
3418
|
+
}
|
|
3419
|
+
getDirections() {
|
|
3420
|
+
return ['ltr', 'rtl'];
|
|
3421
|
+
}
|
|
3422
|
+
getDirectionLabel(dir) {
|
|
3423
|
+
return this.dirLabels.get(dir);
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
DirectionComponent.decorators = [
|
|
3427
|
+
{ type: Component, args: [{
|
|
3428
|
+
selector: 'pb-direction',
|
|
3429
|
+
template: `
|
|
3430
|
+
<mat-form-field appearance="outline">
|
|
3431
|
+
<mat-label i18n="settings|Direction">Direction</mat-label>
|
|
3432
|
+
<mat-select
|
|
3433
|
+
i18n-placeholder="settings|Direction"
|
|
3434
|
+
placeholder="Direction"
|
|
3435
|
+
[(value)]="model.direction"
|
|
3436
|
+
(selectionChange)="markForCheck()"
|
|
3437
|
+
disableRipple
|
|
3438
|
+
>
|
|
3439
|
+
<mat-option *ngFor="let dir of getDirections()" [value]="dir">
|
|
3440
|
+
{{ getDirectionLabel(dir) }}
|
|
3441
|
+
</mat-option>
|
|
3442
|
+
</mat-select>
|
|
3443
|
+
</mat-form-field>
|
|
3444
|
+
`,
|
|
3445
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3446
|
+
styles: [`
|
|
3447
|
+
:host {
|
|
3448
|
+
display: block;
|
|
3449
|
+
width: 100%;
|
|
3450
|
+
}
|
|
3451
|
+
|
|
3452
|
+
mat-form-field {
|
|
3453
|
+
width: 100%;
|
|
3454
|
+
}
|
|
3455
|
+
`]
|
|
3456
|
+
},] }
|
|
3457
|
+
];
|
|
3458
|
+
DirectionComponent.ctorParameters = () => [
|
|
3459
|
+
{ type: PbEmailObjectStoreService }
|
|
3460
|
+
];
|
|
3461
|
+
DirectionComponent.propDecorators = {
|
|
3462
|
+
model: [{ type: Input }]
|
|
3463
|
+
};
|
|
3464
|
+
|
|
3465
|
+
/*
|
|
3466
|
+
* Copyright (c) 2024 Pobuca.
|
|
3467
|
+
* All rights reserved.
|
|
3468
|
+
*/
|
|
3469
|
+
class BackRepatComponent {
|
|
3470
|
+
constructor() {
|
|
3471
|
+
this.disabled = false;
|
|
3472
|
+
this.repeatLabels = new Map([
|
|
3473
|
+
['no-repeat', $localize `:@@no_repeat:No`],
|
|
3474
|
+
['repeat', $localize `:@@repeat:Repeat`],
|
|
3475
|
+
['repeat-x', $localize `:@@repeat_x:X`],
|
|
3476
|
+
['repeat-y', $localize `:@@repeat_y:Y`],
|
|
3477
|
+
['round', $localize `:@@repeat_round:Round`],
|
|
3478
|
+
['space', $localize `:@@repeat_space:Space`]
|
|
3479
|
+
]);
|
|
3480
|
+
}
|
|
3481
|
+
getRepeats() {
|
|
3482
|
+
return ['no-repeat', 'repeat', 'repeat-x', 'repeat-y'];
|
|
3483
|
+
}
|
|
3484
|
+
getRepeatLabel(repeat) {
|
|
3485
|
+
return this.repeatLabels.get(repeat);
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
BackRepatComponent.decorators = [
|
|
3489
|
+
{ type: Component, args: [{
|
|
3490
|
+
selector: 'pb-back-repeat',
|
|
3491
|
+
template: `
|
|
3492
|
+
<mat-form-field appearance="outline">
|
|
3493
|
+
<mat-label i18n="settings|Background Repeat">Repeat</mat-label>
|
|
3494
|
+
<mat-select [(value)]="model.repeat" [disabled]="disabled" disableRipple>
|
|
3495
|
+
<mat-option *ngFor="let repeat of getRepeats()" [value]="repeat">
|
|
3496
|
+
{{ getRepeatLabel(repeat) }}
|
|
3497
|
+
</mat-option>
|
|
3498
|
+
</mat-select>
|
|
3499
|
+
</mat-form-field>
|
|
3500
|
+
`,
|
|
3501
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3502
|
+
styles: [`
|
|
3503
|
+
:host {
|
|
3504
|
+
display: block;
|
|
3505
|
+
width: 100%;
|
|
3506
|
+
}
|
|
3507
|
+
|
|
3508
|
+
mat-form-field {
|
|
3509
|
+
width: 100%;
|
|
3510
|
+
}
|
|
3511
|
+
`]
|
|
3512
|
+
},] }
|
|
3513
|
+
];
|
|
3514
|
+
BackRepatComponent.propDecorators = {
|
|
3515
|
+
model: [{ type: Input }],
|
|
3516
|
+
disabled: [{ type: Input }]
|
|
3517
|
+
};
|
|
3518
|
+
|
|
3519
|
+
/*
|
|
3520
|
+
* Copyright (c) 2024 Pobuca.
|
|
3521
|
+
* All rights reserved.
|
|
3522
|
+
*/
|
|
3523
|
+
const devicesMap = new Map([
|
|
3524
|
+
['desktop', '1 1 100%'],
|
|
3525
|
+
['smartphone', '1 1 360px'],
|
|
3526
|
+
['tablet', '1 1 768px']
|
|
3527
|
+
]);
|
|
3528
|
+
class PreviewTemplateComponent {
|
|
3529
|
+
constructor(middlewares, sanitizer) {
|
|
3530
|
+
this.middlewares = middlewares;
|
|
3531
|
+
this.sanitizer = sanitizer;
|
|
3532
|
+
this.device = 'desktop';
|
|
3533
|
+
}
|
|
3534
|
+
changeDevice(button) {
|
|
3535
|
+
return this.middlewares
|
|
3536
|
+
.togglePreviewDevice(button)
|
|
3537
|
+
.pipe(take(1))
|
|
3538
|
+
.subscribe(device => {
|
|
3539
|
+
this.device = device;
|
|
3540
|
+
});
|
|
3541
|
+
}
|
|
3542
|
+
getFlexWidth() {
|
|
3543
|
+
return devicesMap.get(this.device);
|
|
3544
|
+
}
|
|
3545
|
+
ngOnInit() {
|
|
3546
|
+
this.src = URL.createObjectURL(new Blob([this.template], { type: 'text/html' }));
|
|
3547
|
+
this.templateSrc = this.sanitizer.bypassSecurityTrustResourceUrl(this.src);
|
|
3548
|
+
}
|
|
3549
|
+
ngOnDestroy() {
|
|
3550
|
+
if (this.src) {
|
|
3551
|
+
URL.revokeObjectURL(this.src);
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3554
|
+
}
|
|
3555
|
+
PreviewTemplateComponent.decorators = [
|
|
3556
|
+
{ type: Component, args: [{
|
|
3557
|
+
selector: 'pb-preview-template',
|
|
3558
|
+
template: `
|
|
3559
|
+
<iframe [src]="templateSrc" [fxFlex]="getFlexWidth()"></iframe>
|
|
3560
|
+
<mat-button-toggle-group value="desktop" (change)="changeDevice($event)" vertical>
|
|
3561
|
+
<mat-button-toggle value="desktop" matTooltip="Desktop / 100%" matTooltipPosition="left">
|
|
3562
|
+
<mat-icon>desktop_windows</mat-icon>
|
|
3563
|
+
</mat-button-toggle>
|
|
3564
|
+
<mat-button-toggle value="tablet" matTooltip="Tablet / 768px" matTooltipPosition="left">
|
|
3565
|
+
<mat-icon>tablet</mat-icon>
|
|
3566
|
+
</mat-button-toggle>
|
|
3567
|
+
<mat-button-toggle value="smartphone" matTooltip="Smartphone / 360px" matTooltipPosition="left">
|
|
3568
|
+
<mat-icon>smartphone</mat-icon>
|
|
3569
|
+
</mat-button-toggle>
|
|
3570
|
+
</mat-button-toggle-group>
|
|
3571
|
+
`,
|
|
3572
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3573
|
+
styles: [`
|
|
3574
|
+
iframe {
|
|
3575
|
+
border: 0;
|
|
3576
|
+
will-change: width;
|
|
3577
|
+
transition: all 500ms ease-in-out;
|
|
3578
|
+
}
|
|
3579
|
+
|
|
3580
|
+
/* iframe a[href] {
|
|
3581
|
+
pointer-events: none;
|
|
3582
|
+
} */
|
|
3583
|
+
|
|
3584
|
+
/* iframe.active {
|
|
3585
|
+
opacity: 1;
|
|
3586
|
+
} */
|
|
3587
|
+
|
|
3588
|
+
mat-button-toggle-group {
|
|
3589
|
+
position: absolute;
|
|
3590
|
+
right: 0;
|
|
3591
|
+
background: white;
|
|
3592
|
+
}
|
|
3593
|
+
`]
|
|
3594
|
+
},] }
|
|
3595
|
+
];
|
|
3596
|
+
PreviewTemplateComponent.ctorParameters = () => [
|
|
3597
|
+
{ type: PbUserMiddlewaresService },
|
|
3598
|
+
{ type: DomSanitizer }
|
|
3599
|
+
];
|
|
3600
|
+
PreviewTemplateComponent.propDecorators = {
|
|
3601
|
+
template: [{ type: Input }]
|
|
3602
|
+
};
|
|
3603
|
+
|
|
3604
|
+
/*
|
|
3605
|
+
* Copyright (c) 2024 Pobuca.
|
|
3606
|
+
* All rights reserved.
|
|
3607
|
+
*/
|
|
3608
|
+
class BlockComponent {
|
|
3609
|
+
constructor(pbMiddlewaresService, userInterfaceService, emailObjectStore, chRef) {
|
|
3610
|
+
this.pbMiddlewaresService = pbMiddlewaresService;
|
|
3611
|
+
this.userInterfaceService = userInterfaceService;
|
|
3612
|
+
this.emailObjectStore = emailObjectStore;
|
|
3613
|
+
this.chRef = chRef;
|
|
3614
|
+
this.isActive = false;
|
|
3615
|
+
this.componentIsDestroyed$ = new Subject();
|
|
3616
|
+
}
|
|
3617
|
+
onHostClick(event) {
|
|
3618
|
+
event.stopImmediatePropagation();
|
|
3619
|
+
this.editBlock();
|
|
3620
|
+
}
|
|
3621
|
+
get isBlockActive() {
|
|
3622
|
+
return this.isActive;
|
|
3623
|
+
}
|
|
3624
|
+
removeBlock() {
|
|
3625
|
+
this.pbMiddlewaresService
|
|
3626
|
+
.removeBlock(this.index, this.column, this.block)
|
|
3627
|
+
.pipe(exhaustMap(() => this.userInterfaceService.confirmDialog$()), filter(removeAllowed => removeAllowed), take(1))
|
|
3628
|
+
.subscribe(() => {
|
|
3629
|
+
this.emailObjectStore.removeBlock(this.index, this.column);
|
|
3630
|
+
});
|
|
3631
|
+
}
|
|
3632
|
+
duplicateBlock() {
|
|
3633
|
+
this.pbMiddlewaresService
|
|
3634
|
+
.duplicateBlock(this.index, this.column, this.block)
|
|
3635
|
+
.pipe(map(data => this.emailObjectStore.duplicateBlock(data.index, data.column, data.block)), take(1))
|
|
3636
|
+
.subscribe(() => {
|
|
3637
|
+
// this.chRef.markForCheck();
|
|
3638
|
+
});
|
|
3639
|
+
}
|
|
3640
|
+
editBlock() {
|
|
3641
|
+
this.pbMiddlewaresService
|
|
3642
|
+
.editBlock(this.block)
|
|
3643
|
+
.pipe(take(1))
|
|
3644
|
+
.subscribe(block => {
|
|
3645
|
+
this.userInterfaceService.editBlock(block);
|
|
3646
|
+
});
|
|
3647
|
+
}
|
|
3648
|
+
ngOnInit() {
|
|
3649
|
+
this.userInterfaceService.currentEditingBlock$
|
|
3650
|
+
.pipe(map(currentEditingBlock => currentEditingBlock === this.block), takeUntil(this.componentIsDestroyed$))
|
|
3651
|
+
.subscribe(isActive => {
|
|
3652
|
+
this.isActive = isActive;
|
|
3653
|
+
});
|
|
3654
|
+
}
|
|
3655
|
+
ngDoCheck() {
|
|
3656
|
+
if (this.isActive) {
|
|
3657
|
+
this.chRef.markForCheck();
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3660
|
+
ngOnDestroy() {
|
|
3661
|
+
this.componentIsDestroyed$.next('');
|
|
3662
|
+
this.componentIsDestroyed$.complete();
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3665
|
+
BlockComponent.decorators = [
|
|
3666
|
+
{ type: Component, args: [{
|
|
3667
|
+
selector: 'pb-block',
|
|
3668
|
+
template: "<!--\n ~ Copyright (c) 2024 Pobuca.\n ~ All rights reserved.\n -->\n\n<ng-content select=\".move\"></ng-content>\n<div class=\"tools\" fxLayout fxLayoutGap=\"0.25rem\">\n <button mat-icon-button (click)=\"duplicateBlock()\" i18n-matTooltip=\"block|Duplicate Block\"\n matTooltip=\"Duplicate Block\">\n <mat-icon i18n-aria-label=\"block|Duplicate Block\" aria-label=\"Duplicate block\" inline>file_copy</mat-icon>\n </button>\n <button mat-icon-button (click)=\"removeBlock()\" i18n-matTooltip=\"block|Remove Block\" class=\"delete\"\n matTooltip=\"Remove Block\">\n <mat-icon i18n-aria-label=\"block|Remove Block\" aria-label=\"Delete block\" inline>delete_forever</mat-icon>\n </button>\n</div>\n<ng-content *pbDynamicComponent=\"block\"></ng-content>\n",
|
|
3669
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3670
|
+
styles: ["/*!\n * Copyright (c) 2024 Pobuca.\n * All rights reserved.\n */:host{display:block;position:relative}:host.active:not(.cdk-drag-preview)>.tools{opacity:1;display:flex!important}:host.active,:host.cdk-drag-placeholder,:host.cdk-drag-preview{z-index:3;box-shadow:0 0 0 2px #3f51b5}:host:hover:not(.active){box-shadow:0 0 0 2px rgba(63,81,181,.5)}:host>.tools{direction:ltr;opacity:0;z-index:4;position:absolute;max-width:100%;bottom:-25px;right:0;display:none!important;transition:all .5s ease-in-out}:host>.tools>button{background:#3f51b5;border-radius:3px;color:#fff;line-height:0;width:25px;height:25px}:host>.tools>button mat-icon{font-size:14px;margin-top:-3px}:host>.tools ::ng-deep+*>*{transition:all .5s cubic-bezier(.445,.05,.55,.95)}"]
|
|
3671
|
+
},] }
|
|
3672
|
+
];
|
|
3673
|
+
BlockComponent.ctorParameters = () => [
|
|
3674
|
+
{ type: PbUserMiddlewaresService },
|
|
3675
|
+
{ type: PbUserInterfaceService },
|
|
3676
|
+
{ type: PbEmailObjectStoreService },
|
|
3677
|
+
{ type: ChangeDetectorRef }
|
|
3678
|
+
];
|
|
3679
|
+
BlockComponent.propDecorators = {
|
|
3680
|
+
block: [{ type: Input }],
|
|
3681
|
+
column: [{ type: Input }],
|
|
3682
|
+
index: [{ type: Input }],
|
|
3683
|
+
onHostClick: [{ type: HostListener, args: ['click', ['$event'],] }],
|
|
3684
|
+
isBlockActive: [{ type: HostBinding, args: ['class.active',] }]
|
|
3685
|
+
};
|
|
3686
|
+
|
|
3687
|
+
/*
|
|
3688
|
+
* Copyright (c) 2024 Pobuca.
|
|
3689
|
+
* All rights reserved.
|
|
3690
|
+
*/
|
|
3691
|
+
class ImageUploadComponent {
|
|
3692
|
+
constructor(imageUpload, emailObject) {
|
|
3693
|
+
this.imageUpload = imageUpload;
|
|
3694
|
+
this.emailObject = emailObject;
|
|
3695
|
+
this.key = 'src';
|
|
3696
|
+
this.browsing = new Subject();
|
|
3697
|
+
}
|
|
3698
|
+
get source() {
|
|
3699
|
+
return this.block[this.key];
|
|
3700
|
+
}
|
|
3701
|
+
set source(value) {
|
|
3702
|
+
this.block[this.key] = value;
|
|
3703
|
+
}
|
|
3704
|
+
markForCheck() {
|
|
3705
|
+
this.emailObject.markForCheck();
|
|
3706
|
+
}
|
|
3707
|
+
browse() {
|
|
3708
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3709
|
+
this.browsing.next(true);
|
|
3710
|
+
try {
|
|
3711
|
+
const path = yield this.imageUpload.browse$().toPromise();
|
|
3712
|
+
if (path) {
|
|
3713
|
+
this.source = path;
|
|
3714
|
+
this.emailObject.markForCheck();
|
|
3715
|
+
}
|
|
3716
|
+
}
|
|
3717
|
+
finally {
|
|
3718
|
+
this.browsing.next(false);
|
|
3719
|
+
}
|
|
3720
|
+
});
|
|
3721
|
+
}
|
|
3722
|
+
ngOnInit() {
|
|
3723
|
+
this.source = this.block.src || this.block.url;
|
|
3724
|
+
}
|
|
3725
|
+
}
|
|
3726
|
+
ImageUploadComponent.decorators = [
|
|
3727
|
+
{ type: Component, args: [{
|
|
3728
|
+
selector: 'pb-image-upload',
|
|
3729
|
+
template: `
|
|
3730
|
+
<div fxLayout fxLayoutAlign="space-between center">
|
|
3731
|
+
<div class="image-info" [fxFlex]="source ? '30%' : '60%'" fxLayout fxLayoutGap="0.5em" fxLayoutAlign="center center">
|
|
3732
|
+
<div *ngIf="source" class="chose-image">
|
|
3733
|
+
<img [src]="source" alt="image source" />
|
|
3734
|
+
</div>
|
|
3735
|
+
<span *ngIf="!source" style="width: 100%;" i18n="Upload Image Settings|Upload an image"> Upload an image </span>
|
|
3736
|
+
</div>
|
|
3737
|
+
<button
|
|
3738
|
+
type="button"
|
|
3739
|
+
(click)="browse()"
|
|
3740
|
+
[disabled]="browsing | async"
|
|
3741
|
+
color="primary"
|
|
3742
|
+
mat-stroked-button
|
|
3743
|
+
i18n="Upload Image Settings|Browse"
|
|
3744
|
+
>
|
|
3745
|
+
Browse
|
|
3746
|
+
</button>
|
|
3747
|
+
</div>
|
|
3748
|
+
<mat-form-field appearance="outline" style="width: 100%">
|
|
3749
|
+
<mat-label i18n="Upload Image Settings|Image Source">Image Source</mat-label>
|
|
3750
|
+
<input matInput [(ngModel)]="source" type="url" (input)="markForCheck()" />
|
|
3751
|
+
</mat-form-field>
|
|
3752
|
+
`,
|
|
3753
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3754
|
+
styles: [`
|
|
3755
|
+
:host {
|
|
3756
|
+
display: block;
|
|
3757
|
+
}
|
|
3758
|
+
|
|
3759
|
+
.chose-image {
|
|
3760
|
+
width: 100%;
|
|
3761
|
+
max-height: 60px;
|
|
3762
|
+
overflow: hidden;
|
|
3763
|
+
border-radius: 3px;
|
|
3764
|
+
line-height: 0;
|
|
3765
|
+
}
|
|
3766
|
+
|
|
3767
|
+
.chose-image img {
|
|
3768
|
+
width: 100%;
|
|
3769
|
+
height: 60px;
|
|
3770
|
+
object-fit: cover;
|
|
3771
|
+
object-position: center;
|
|
3772
|
+
}
|
|
3773
|
+
|
|
3774
|
+
.size {
|
|
3775
|
+
font-size: 80%;
|
|
3776
|
+
color: #ccc;
|
|
3777
|
+
}
|
|
3778
|
+
|
|
3779
|
+
mat-form-field {
|
|
3780
|
+
margin-top: 0.5rem;
|
|
3781
|
+
}
|
|
3782
|
+
`]
|
|
3783
|
+
},] }
|
|
3784
|
+
];
|
|
3785
|
+
ImageUploadComponent.ctorParameters = () => [
|
|
3786
|
+
{ type: PbUserImageUploaderService },
|
|
3787
|
+
{ type: PbEmailObjectStoreService }
|
|
3788
|
+
];
|
|
3789
|
+
ImageUploadComponent.propDecorators = {
|
|
3790
|
+
block: [{ type: Input }],
|
|
3791
|
+
key: [{ type: Input }]
|
|
3792
|
+
};
|
|
3793
|
+
|
|
3794
|
+
/*
|
|
3795
|
+
* Copyright (c) 2024 Pobuca.
|
|
3796
|
+
* All rights reserved.
|
|
3797
|
+
*/
|
|
3798
|
+
class BlockSettingsComponent {
|
|
3799
|
+
constructor(internalService, emailObject, networks) {
|
|
3800
|
+
this.internalService = internalService;
|
|
3801
|
+
this.emailObject = emailObject;
|
|
3802
|
+
this.networks = networks;
|
|
3803
|
+
this.block$ = this.internalService.currentEditingBlock$;
|
|
3804
|
+
}
|
|
3805
|
+
hasProperty(options, property) {
|
|
3806
|
+
return options.hasOwnProperty(property);
|
|
3807
|
+
}
|
|
3808
|
+
markForCheck() {
|
|
3809
|
+
this.emailObject.markForCheck();
|
|
3810
|
+
}
|
|
3811
|
+
socialNetworks(block) {
|
|
3812
|
+
return this.networks.filter(link => !block.networks.find(({ name }) => name === link)).sort();
|
|
3813
|
+
}
|
|
3814
|
+
addSocialNetwork({ value, source }, block) {
|
|
3815
|
+
block.networks.push({ name: value, href: '', label: '' });
|
|
3816
|
+
source.value = '';
|
|
3817
|
+
this.markForCheck();
|
|
3818
|
+
}
|
|
3819
|
+
removeSocialNetwork(network, block) {
|
|
3820
|
+
block.networks = block.networks.filter(n => n !== network);
|
|
3821
|
+
this.markForCheck();
|
|
3822
|
+
}
|
|
3823
|
+
getSocialIcon(network) {
|
|
3824
|
+
return getAssetByPath(`${network}.png`);
|
|
3825
|
+
}
|
|
3826
|
+
}
|
|
3827
|
+
BlockSettingsComponent.decorators = [
|
|
3828
|
+
{ type: Component, args: [{
|
|
3829
|
+
selector: 'pb-block-settings',
|
|
3830
|
+
template: "<!--\n ~ Copyright (c) 2024 Pobuca.\n ~ All rights reserved.\n -->\n\n<ng-container *ngIf=\"block$ | async as currentBlock\">\n <mat-accordion>\n <mat-expansion-panel expanded *ngIf=\"hasProperty(currentBlock.options, 'font')\">\n <mat-expansion-panel-header i18n=\"settings|Font\">\n Font\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <pb-color [options]=\"currentBlock.options\" key=\"color\"></pb-color>\n <pb-font-styles [font]=\"currentBlock.options.font\"></pb-font-styles>\n <pb-line-height *ngIf=\"currentBlock.type !== 'social'\" [lineHeight]=\"currentBlock.options.lineHeight\">\n </pb-line-height>\n </ng-template>\n </mat-expansion-panel>\n\n <ng-container *ngIf=\"currentBlock.type === 'image'\">\n <mat-expansion-panel expanded>\n <mat-expansion-panel-header i18n=\"settings|Image\">\n Image\n </mat-expansion-panel-header>\n <pb-image-upload [block]=\"currentBlock\"></pb-image-upload>\n </mat-expansion-panel>\n\n <mat-expansion-panel>\n <mat-expansion-panel-header i18n=\"settings|Image Attributes\">\n Attributes\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <div class=\"group f-large\">\n <mat-form-field appearance=\"outline\">\n <mat-label i18n=\"settings|Image Alt\">Alt</mat-label>\n <input matInput [(ngModel)]=\"currentBlock.options.title\" type=\"text\" i18n-placeholder\n placeholder=\"settings|Image Alt\" (input)=\"markForCheck()\" />\n </mat-form-field>\n <pb-align [model]=\"currentBlock.options\"></pb-align>\n </div>\n <pb-link [link]=\"currentBlock.options.link\"></pb-link>\n </ng-template>\n </mat-expansion-panel>\n </ng-container>\n\n <ng-container *ngIf=\"currentBlock.type === 'button'\">\n <mat-expansion-panel>\n <mat-expansion-panel-header i18n=\"settings|Button Text\">\n Button Text\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <mat-form-field appearance=\"outline\" style=\"width: 100%\">\n <mat-label i18n=\"settings|Button Text\">Button Text</mat-label>\n <input matInput [(ngModel)]=\"currentBlock.innerText\" type=\"text\" i18n-placeholder\n placeholder=\"settings|Button Text\" (input)=\"markForCheck()\" />\n </mat-form-field>\n </ng-template>\n </mat-expansion-panel>\n\n\n <mat-expansion-panel>\n <mat-expansion-panel-header i18n=\"settings|Background\">\n Background\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <pb-color [options]=\"currentBlock.options\"></pb-color>\n </ng-template>\n </mat-expansion-panel>\n\n <mat-expansion-panel>\n <mat-expansion-panel-header i18n=\"settings|Button Attributes\">\n Attributes\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <div class=\"group two\" style=\"padding-bottom: 0\">\n <pb-align [model]=\"currentBlock.options\" [disabled]=\"currentBlock.options.fullWidth\"></pb-align>\n <mat-slide-toggle style=\"margin-top: -24px\" [(ngModel)]=\"currentBlock.options.fullWidth\"\n (change)=\"markForCheck()\">\n <ng-container i18n=\"settings|Button Full Width\">Full Width</ng-container>\n </mat-slide-toggle>\n </div>\n <pb-link [link]=\"currentBlock.options.link\"></pb-link>\n <pb-padding [padding]=\"currentBlock.options.innerPadding\"></pb-padding>\n </ng-template>\n </mat-expansion-panel>\n </ng-container>\n\n <mat-expansion-panel [expanded]=\"currentBlock.type === 'spacer'\"\n *ngIf=\"hasProperty(currentBlock.options, 'width') || hasProperty(currentBlock.options, 'height')\">\n <mat-expansion-panel-header i18n=\"settings|Sizes\">\n Sizes\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <pb-width-height *ngIf=\"hasProperty(currentBlock.options, 'width')\" [model]=\"currentBlock.options.width\"\n label=\"width\"></pb-width-height>\n <pb-width-height *ngIf=\"hasProperty(currentBlock.options, 'height')\" [model]=\"currentBlock.options.height\"\n label=\"height\"></pb-width-height>\n </ng-template>\n </mat-expansion-panel>\n\n <mat-expansion-panel *ngIf=\"hasProperty(currentBlock.options, 'background')\">\n <mat-expansion-panel-header i18n=\"settings|Background\">\n Background\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <pb-color [options]=\"currentBlock.options.background\"></pb-color>\n </ng-template>\n backgroundColor\n </mat-expansion-panel>\n\n <mat-expansion-panel [expanded]=\"currentBlock.type === 'divider'\"\n *ngIf=\"hasProperty(currentBlock.options, 'border')\">\n <mat-expansion-panel-header>\n <ng-container i18n=\"settings|Divider Styles\" *ngIf=\"currentBlock.type === 'divider'\">Styles</ng-container>\n <ng-container i18n=\"settings|Border\" *ngIf=\"currentBlock.type !== 'divider'\">Border</ng-container>\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <pb-border [border]=\"currentBlock.options.border\"></pb-border>\n </ng-template>\n </mat-expansion-panel>\n\n <ng-container *ngIf=\"currentBlock.type === 'social'\">\n <mat-expansion-panel>\n <mat-expansion-panel-header i18n=\"settings|Icons\">\n Icons\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <mat-form-field appearance=\"outline\" style=\"width: 100%\">\n <mat-label i18n=\"settings|Add new Network\">Add new Network</mat-label>\n <mat-select (selectionChange)=\"addSocialNetwork($event, currentBlock)\" [disabled]=\"!socialNetworks.length\">\n <mat-option style=\"text-transform: capitalize\" *ngFor=\"let link of socialNetworks(currentBlock)\"\n [value]=\"link\">\n {{ link }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </ng-template>\n </mat-expansion-panel>\n\n <mat-expansion-panel class=\"networks-list\" *ngFor=\"let network of currentBlock.networks\">\n <mat-expansion-panel-header style=\"text-transform: capitalize\">\n <img [class]=\"network.name\" [src]=\"getSocialIcon(network.name)\" [alt]=\"network.name\" />\n {{ network.name }}\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <div class=\"networks-list-item\">\n <div class=\"item-info\">\n <mat-form-field matLine appearance=\"outline\">\n <mat-label i18n=\"settings|Social Network Label\">Label</mat-label>\n <input matInput autocomplete=\"off\" [(ngModel)]=\"network.label\"\n i18n-placeholder=\"settings|Social Network Label\" placeholder=\"Label\" type=\"text\"\n (input)=\"markForCheck()\" />\n </mat-form-field>\n <button mat-stroked-button color=\"warn\" (click)=\"removeSocialNetwork(network, currentBlock)\">\n <mat-icon>delete_forever</mat-icon>\n </button>\n </div>\n <mat-form-field matLine appearance=\"outline\">\n <mat-label style=\"text-transform: capitalize\">\n {{ network.name }} <ng-container i18n=\"settings|Social Network Link\">Link</ng-container>\n </mat-label>\n <input autocomplete=\"off\" matInput [(ngModel)]=\"network.href\" type=\"url\" (input)=\"markForCheck()\" />\n </mat-form-field>\n </div>\n </ng-template>\n </mat-expansion-panel>\n\n <mat-expansion-panel>\n <mat-expansion-panel-header i18n=\"settings|Social Styles\">\n Styles\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <div class=\"group two\" style=\"padding-bottom: 0\">\n <mat-form-field appearance=\"outline\">\n <mat-label i18n=\"settings|Social Model\">Mode</mat-label>\n <mat-select (selectionChange)=\"markForCheck()\" i18n-placeholder=\"settings|Social Model\" placeholder=\"Mode\"\n [(value)]=\"currentBlock.options.mode\" disableRipple>\n <mat-option *ngFor=\"let mode of ['horizontal', 'vertical']\" [value]=\"mode\">\n {{ mode }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n <pb-align [model]=\"currentBlock.options\"></pb-align>\n </div>\n <pb-line-height [units]=\"['px']\" [lineHeight]=\"currentBlock.options.iconSize\" label=\"Icon Size\">\n </pb-line-height>\n <pb-padding [padding]=\"currentBlock.options.innerPadding\"></pb-padding>\n </ng-template>\n </mat-expansion-panel>\n </ng-container>\n\n <mat-expansion-panel *ngIf=\"hasProperty(currentBlock.options, 'padding')\">\n <mat-expansion-panel-header i18n=\"settings|Padding\">\n Padding\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <pb-padding [padding]=\"currentBlock.options.padding\"></pb-padding>\n </ng-template>\n </mat-expansion-panel>\n </mat-accordion>\n</ng-container>\n",
|
|
3831
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3832
|
+
encapsulation: ViewEncapsulation.None,
|
|
3833
|
+
styles: ["/*!\n * Copyright (c) 2024 Pobuca.\n * All rights reserved.\n */.networks-list mat-expansion-panel-header .mat-content{place-items:center}.networks-list mat-expansion-panel-header img{width:20px;margin:0 .5rem 0 0;border-radius:3px}.networks-list mat-expansion-panel-header img.facebook{background-color:#3a5898}.networks-list mat-expansion-panel-header img.twitter{background-color:#d95988}.networks-list mat-expansion-panel-header img.github{background-color:#000}.networks-list mat-expansion-panel-header img.instagram{background-color:#3f729b}.networks-list mat-expansion-panel-header img.web{background-color:#4bade9}.networks-list mat-expansion-panel-header img.snapchat{background-color:#fffa54}.networks-list mat-expansion-panel-header img.youtube{background-color:#ea3323}.networks-list mat-expansion-panel-header img.vimeo{background-color:#53b4e7}.networks-list mat-expansion-panel-header img.medium{background-color:#000}.networks-list mat-expansion-panel-header img.soundcloud{background-color:#ef7f31}.networks-list mat-expansion-panel-header img.dribbble{background-color:#d95988}.networks-list mat-expansion-panel-header img.pinterest{background-color:#bd071c}.networks-list mat-expansion-panel-header img.linkedin{background-color:#0077b5}.networks-list mat-expansion-panel-header img.tumblr{background-color:#334356}.networks-list mat-expansion-panel-header img.xing{background-color:#296365}.networks-list mat-expansion-panel-header img.tiktok{background-color:#000}.networks-list-item{width:100%;display:grid;row-gap:.5rem}.networks-list-item .item-info{display:grid;grid-template-columns:2fr 1fr;-moz-column-gap:.5rem;column-gap:.5rem}.networks-list-item .item-info button{align-self:baseline;margin:.25rem 0;height:50px}.networks-list-item .item-info ::ng-deep .mat-form-field-wrapper{padding-bottom:0;margin-bottom:0}"]
|
|
3834
|
+
},] }
|
|
3835
|
+
];
|
|
3836
|
+
BlockSettingsComponent.ctorParameters = () => [
|
|
3837
|
+
{ type: PbUserInterfaceService },
|
|
3838
|
+
{ type: PbEmailObjectStoreService },
|
|
3839
|
+
{ type: Array, decorators: [{ type: Inject, args: [PB_SOCIAL_NETWORKS,] }] }
|
|
3840
|
+
];
|
|
3841
|
+
|
|
3842
|
+
/*
|
|
3843
|
+
* Copyright (c) 2024 Pobuca.
|
|
3844
|
+
* All rights reserved.
|
|
3845
|
+
*/
|
|
3846
|
+
class StructureSettingsComponent {
|
|
3847
|
+
constructor(pbInternalService, emailObject) {
|
|
3848
|
+
this.pbInternalService = pbInternalService;
|
|
3849
|
+
this.emailObject = emailObject;
|
|
3850
|
+
this.currentStructure$ = this.pbInternalService.currentEditingStructure$;
|
|
3851
|
+
}
|
|
3852
|
+
markForCheck() {
|
|
3853
|
+
this.emailObject.markForCheck();
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
StructureSettingsComponent.decorators = [
|
|
3857
|
+
{ type: Component, args: [{
|
|
3858
|
+
selector: 'pb-structure-settings',
|
|
3859
|
+
template: "<!--\n ~ Copyright (c) 2024 Pobuca.\n ~ All rights reserved.\n -->\n\n<mat-accordion *ngIf=\"currentStructure$ | async as structure\">\n <mat-expansion-panel expanded>\n <mat-expansion-panel-header i18n=\"Structure settings|Change structure Background\">\n Background\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <pb-color [options]=\"structure.options.background\" key=\"color\"></pb-color>\n <pb-image-upload [block]=\"structure.options.background\" key=\"url\" style=\"margin-bottom: 1em\"></pb-image-upload>\n <pb-back-repeat [disabled]=\"!structure.options.background.url\"\n [model]=\"structure.options.background\"></pb-back-repeat>\n <pb-width-height [disabled]=\"!structure.options.background.url\" [model]=\"structure.options.background.size\"\n label=\"size\">\n </pb-width-height>\n </ng-template>\n </mat-expansion-panel>\n\n <mat-expansion-panel>\n <mat-expansion-panel-header i18n=\"Structure settings|Change structure border\">\n Border\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <pb-border [border]=\"structure.options.border\"></pb-border>\n </ng-template>\n </mat-expansion-panel>\n\n <mat-expansion-panel>\n <mat-expansion-panel-header i18n=\"Structure settings|Change structure padding\">\n Padding\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <pb-padding [padding]=\"structure.options.padding\"></pb-padding>\n </ng-template>\n </mat-expansion-panel>\n\n <mat-expansion-panel>\n <mat-expansion-panel-header i18n=\"Structure settings|Change structure margin\">\n Margin\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <pb-margin [margin]=\"structure.options.margin\"></pb-margin>\n </ng-template>\n </mat-expansion-panel>\n\n <mat-expansion-panel>\n <mat-expansion-panel-header i18n=\"Structure settings|Change structure layout\">\n Layout\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <pb-gaps [gaps]=\"structure.options.gaps\"></pb-gaps>\n <mat-slide-toggle [disabled]=\"structure.type === 'cols_1'\" style=\"margin-top: -24px;\"\n [(ngModel)]=\"structure.options.disableResponsive\" i18n=\"Structure settings|Disable Responsive\"\n (change)=\"markForCheck()\">\n Disable Responsive\n </mat-slide-toggle>\n <mat-slide-toggle\n [(ngModel)]=\"structure.options.fullWidth\" i18n=\"Structure settings|Full Width\"\n (change)=\"markForCheck()\">\n Make section full width\n </mat-slide-toggle>\n </ng-template>\n </mat-expansion-panel>\n\n <mat-expansion-panel *ngFor=\"let column of structure.options.columns; let key = index\">\n <mat-expansion-panel-header>\n <ng-container i18n=\"Structure settings|Structure Column\">Column</ng-container>\n {{ key + 1 }}\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <h3 class=\"divider\">\n <span i18n=\"Structure settings|Column Background\">Column Background</span>\n </h3>\n <pb-color [options]=\"column.background\" [showTransparent]=\"true\" key=\"color\"></pb-color>\n <h3 class=\"divider\">\n <span i18n=\"Structure settings|Column Border\">Column Border</span>\n </h3>\n <pb-border [border]=\"column.border\"></pb-border>\n <h3 class=\"divider\">\n <span i18n=\"Structure settings|Column Vertical Align\">Vertical Align</span>\n </h3>\n <pb-align [model]=\"column\" mode=\"vertical\"></pb-align>\n </ng-template>\n </mat-expansion-panel>\n</mat-accordion>\n",
|
|
3860
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3861
|
+
encapsulation: ViewEncapsulation.None,
|
|
3862
|
+
styles: [":host{display:block}"]
|
|
3863
|
+
},] }
|
|
3864
|
+
];
|
|
3865
|
+
StructureSettingsComponent.ctorParameters = () => [
|
|
3866
|
+
{ type: PbUserInterfaceService },
|
|
3867
|
+
{ type: PbEmailObjectStoreService }
|
|
3868
|
+
];
|
|
3869
|
+
|
|
3870
|
+
/*
|
|
3871
|
+
* Copyright (c) 2024 Pobuca.
|
|
3872
|
+
* All rights reserved.
|
|
3873
|
+
*/
|
|
3874
|
+
class GeneralSettingsComponent {
|
|
3875
|
+
constructor(pbEmailObject) {
|
|
3876
|
+
this.pbEmailObject = pbEmailObject;
|
|
3877
|
+
this.generalOptions$ = this.pbEmailObject.generalEmailOptionsAsObservable$;
|
|
3878
|
+
}
|
|
3879
|
+
}
|
|
3880
|
+
GeneralSettingsComponent.decorators = [
|
|
3881
|
+
{ type: Component, args: [{
|
|
3882
|
+
selector: 'pb-general-settings',
|
|
3883
|
+
template: "<!--\n ~ Copyright (c) 2024 Pobuca.\n ~ All rights reserved.\n -->\n\n<ng-container *ngIf=\"generalOptions$ | async as general\">\n <div class=\"mat-expansion-panel-body\" style=\"margin: 24px 0\">\n <mat-form-field appearance=\"outline\" style=\"width: 100%\">\n <mat-label i18n=\"General Settings|Email Name\">Email Name</mat-label>\n <input matInput [(ngModel)]=\"general.name\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\" style=\"width: 100%\">\n <mat-label i18n=\"General Settings|Email Preview Text\">Preview Text</mat-label>\n <textarea matInput [(ngModel)]=\"general.previewText\"></textarea>\n <mat-hint i18n=\"General Settings|Email Preview Text Hint\">\n This text is displayed in the inbox of the recipient.\n </mat-hint>\n </mat-form-field>\n </div>\n\n <mat-accordion>\n <mat-expansion-panel>\n <mat-expansion-panel-header i18n=\"General Settings|Email Background\">\n Background\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <pb-color [options]=\"general.background\" key=\"color\"></pb-color>\n <!-- <pb-image-upload [block]=\"structure.options.background\" key=\"url\" style=\"margin-bottom: 1em\"></pb-image-upload>\n <pb-back-repeat [disabled]=\"!structure.options.background.url\" [model]=\"structure.options.background\">\n </pb-back-repeat>\n <pb-width-height [model]=\"structure.options.background.size\" label=\"Background Size\"></pb-width-height> -->\n </ng-template>\n </mat-expansion-panel>\n\n <mat-expansion-panel>\n <mat-expansion-panel-header i18n=\"General Settings|Email Attributes\">\n Attributes\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <pb-width-height [model]=\"general.width\" label=\"width\"></pb-width-height>\n <pb-direction [model]=\"general\"></pb-direction>\n </ng-template>\n </mat-expansion-panel>\n\n <mat-expansion-panel>\n <mat-expansion-panel-header i18n=\"General Settings|Email Padding\">\n Padding\n </mat-expansion-panel-header>\n <ng-template matExpansionPanelContent>\n <pb-padding [padding]=\"general.padding\"></pb-padding>\n </ng-template>\n </mat-expansion-panel>\n </mat-accordion>\n</ng-container>\n",
|
|
3884
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3885
|
+
encapsulation: ViewEncapsulation.None,
|
|
3886
|
+
styles: [""]
|
|
3887
|
+
},] }
|
|
3888
|
+
];
|
|
3889
|
+
GeneralSettingsComponent.ctorParameters = () => [
|
|
3890
|
+
{ type: PbEmailObjectStoreService }
|
|
3891
|
+
];
|
|
3892
|
+
|
|
3893
|
+
/*
|
|
3894
|
+
* Copyright (c) 2024 Pobuca.
|
|
3895
|
+
* All rights reserved.
|
|
3896
|
+
*/
|
|
3897
|
+
class MarginComponent {
|
|
3898
|
+
constructor(emailObject) {
|
|
3899
|
+
this.emailObject = emailObject;
|
|
3900
|
+
}
|
|
3901
|
+
markForCheck() {
|
|
3902
|
+
this.emailObject.markForCheck();
|
|
3903
|
+
}
|
|
3904
|
+
}
|
|
3905
|
+
MarginComponent.decorators = [
|
|
3906
|
+
{ type: Component, args: [{
|
|
3907
|
+
selector: 'pb-margin',
|
|
3908
|
+
template: `
|
|
3909
|
+
<div class="group">
|
|
3910
|
+
<mat-form-field appearance="outline">
|
|
3911
|
+
<mat-label i18n="Margin settings|Margin Top">Margin Top</mat-label>
|
|
3912
|
+
<input matInput type="number" min="0" step="1" [(ngModel)]="margin.top" (input)="markForCheck()" />
|
|
3913
|
+
</mat-form-field>
|
|
3914
|
+
<mat-form-field appearance="outline">
|
|
3915
|
+
<mat-label i18n="Margin settings|Margin Bottom">Margin Bottom</mat-label>
|
|
3916
|
+
<input matInput type="number" min="0" step="1" [(ngModel)]="margin.bottom" (input)="markForCheck()" />
|
|
3917
|
+
</mat-form-field>
|
|
3918
|
+
</div>
|
|
3919
|
+
`,
|
|
3920
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3921
|
+
styles: [`
|
|
3922
|
+
:host {
|
|
3923
|
+
display: block;
|
|
3924
|
+
width: 100%;
|
|
3925
|
+
}
|
|
3926
|
+
`]
|
|
3927
|
+
},] }
|
|
3928
|
+
];
|
|
3929
|
+
MarginComponent.ctorParameters = () => [
|
|
3930
|
+
{ type: PbEmailObjectStoreService }
|
|
3931
|
+
];
|
|
3932
|
+
MarginComponent.propDecorators = {
|
|
3933
|
+
margin: [{ type: Input }]
|
|
3934
|
+
};
|
|
3935
|
+
|
|
3936
|
+
/*
|
|
3937
|
+
* Copyright (c) 2024 Pobuca.
|
|
3938
|
+
* All rights reserved.
|
|
3939
|
+
*/
|
|
3940
|
+
class GapsComponent {
|
|
3941
|
+
constructor(emailObject) {
|
|
3942
|
+
this.emailObject = emailObject;
|
|
3943
|
+
}
|
|
3944
|
+
markForCheck() {
|
|
3945
|
+
this.emailObject.markForCheck();
|
|
3946
|
+
}
|
|
3947
|
+
}
|
|
3948
|
+
GapsComponent.decorators = [
|
|
3949
|
+
{ type: Component, args: [{
|
|
3950
|
+
selector: 'pb-gaps',
|
|
3951
|
+
template: `
|
|
3952
|
+
<div class="group">
|
|
3953
|
+
<mat-form-field appearance="outline">
|
|
3954
|
+
<mat-label i18n="Gaps settings|Vertical Gaps">Vertical Gaps</mat-label>
|
|
3955
|
+
<input matInput type="number" min="0" step="1" [(ngModel)]="gaps[0]" (input)="markForCheck()" />
|
|
3956
|
+
</mat-form-field>
|
|
3957
|
+
<mat-form-field appearance="outline">
|
|
3958
|
+
<mat-label i18n="Gaps settings|Horizontal Gaps">Horizontal Gaps</mat-label>
|
|
3959
|
+
<input matInput type="number" min="0" step="1" [(ngModel)]="gaps[1]" (input)="markForCheck()" />
|
|
3960
|
+
</mat-form-field>
|
|
3961
|
+
</div>
|
|
3962
|
+
`,
|
|
3963
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3964
|
+
styles: [`
|
|
3965
|
+
:host {
|
|
3966
|
+
display: block;
|
|
3967
|
+
width: 100%;
|
|
3968
|
+
}
|
|
3969
|
+
`]
|
|
3970
|
+
},] }
|
|
3971
|
+
];
|
|
3972
|
+
GapsComponent.ctorParameters = () => [
|
|
3973
|
+
{ type: PbEmailObjectStoreService }
|
|
3974
|
+
];
|
|
3975
|
+
GapsComponent.propDecorators = {
|
|
3976
|
+
gaps: [{ type: Input }]
|
|
3977
|
+
};
|
|
3978
|
+
|
|
3979
|
+
/*
|
|
3980
|
+
* Copyright (c) 2024 Pobuca.
|
|
3981
|
+
* All rights reserved.
|
|
3982
|
+
*/
|
|
3983
|
+
/**
|
|
3984
|
+
* @ignore
|
|
3985
|
+
*/
|
|
3986
|
+
const NOT_ALLOWED = 'This feature is disabled in free version.';
|
|
3987
|
+
/**
|
|
3988
|
+
* @internal
|
|
3989
|
+
*/
|
|
3990
|
+
class FreeUsersMiddleware extends PbUserMiddlewaresService {
|
|
3991
|
+
constructor() {
|
|
3992
|
+
super();
|
|
3993
|
+
// tslint:disable-next-line: no-console
|
|
3994
|
+
console.warn('You use the Regular License of Angular Email Builder, some limitations are applied. More info on https://wlocalhost.org');
|
|
3995
|
+
}
|
|
3996
|
+
chooseTemplate() {
|
|
3997
|
+
throw new Error(NOT_ALLOWED);
|
|
3998
|
+
}
|
|
3999
|
+
exportFile(type) {
|
|
4000
|
+
return iif(() => type === 'json', deferOf(type), throwError(new Error(NOT_ALLOWED)));
|
|
4001
|
+
}
|
|
4002
|
+
importFile(file) {
|
|
4003
|
+
return iif(() => file.type === 'application/json', deferOf(file), throwError(new Error(NOT_ALLOWED)));
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
4006
|
+
|
|
4007
|
+
/*
|
|
4008
|
+
* Copyright (c) 2024 Pobuca.
|
|
4009
|
+
* All rights reserved.
|
|
4010
|
+
*/
|
|
4011
|
+
/**
|
|
4012
|
+
* @internal
|
|
4013
|
+
* No limitations are applied for Paid users
|
|
4014
|
+
*/
|
|
4015
|
+
class PaidUsersMiddleware extends PbUserMiddlewaresService {
|
|
4016
|
+
}
|
|
4017
|
+
|
|
4018
|
+
/*
|
|
4019
|
+
* Copyright (c) 2024 Pobuca.
|
|
4020
|
+
* All rights reserved.
|
|
4021
|
+
*/
|
|
4022
|
+
|
|
4023
|
+
/*
|
|
4024
|
+
* Copyright (c) 2024 Pobuca.
|
|
4025
|
+
* All rights reserved.
|
|
4026
|
+
*/
|
|
4027
|
+
class IpInterceptorInterceptor {
|
|
4028
|
+
constructor(userConfig, userInterface) {
|
|
4029
|
+
this.userConfig = userConfig;
|
|
4030
|
+
this.userInterface = userInterface;
|
|
4031
|
+
}
|
|
4032
|
+
intercept(request, next) {
|
|
4033
|
+
if (request.url.startsWith('https://ngb-api.wlocalhost.org')) {
|
|
4034
|
+
const notify = this.userInterface.notify('Loading, please wait ...', null, null);
|
|
4035
|
+
const { xApiKey } = Object.assign(Object.assign({}, PB_DEFAULT_CONFIG), this.userConfig);
|
|
4036
|
+
return next
|
|
4037
|
+
.handle(request.clone({
|
|
4038
|
+
setHeaders: {
|
|
4039
|
+
'Content-Type': 'application/json',
|
|
4040
|
+
'X-Api-Key': xApiKey
|
|
4041
|
+
},
|
|
4042
|
+
responseType: 'json'
|
|
4043
|
+
}))
|
|
4044
|
+
.pipe(tap((res) => {
|
|
4045
|
+
if (res.type === HttpEventType.Response) {
|
|
4046
|
+
notify.dismiss();
|
|
4047
|
+
}
|
|
4048
|
+
}));
|
|
4049
|
+
}
|
|
4050
|
+
else {
|
|
4051
|
+
return next.handle(request);
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
4055
|
+
IpInterceptorInterceptor.ɵprov = i0.ɵɵdefineInjectable({ factory: function IpInterceptorInterceptor_Factory() { return new IpInterceptorInterceptor(i0.ɵɵinject(PB_CONFIG), i0.ɵɵinject(PbUserInterfaceService)); }, token: IpInterceptorInterceptor, providedIn: "root" });
|
|
4056
|
+
IpInterceptorInterceptor.decorators = [
|
|
4057
|
+
{ type: Injectable, args: [{
|
|
4058
|
+
providedIn: 'root'
|
|
4059
|
+
},] }
|
|
4060
|
+
];
|
|
4061
|
+
IpInterceptorInterceptor.ctorParameters = () => [
|
|
4062
|
+
{ type: undefined, decorators: [{ type: Inject, args: [PB_CONFIG,] }] },
|
|
4063
|
+
{ type: PbUserInterfaceService }
|
|
4064
|
+
];
|
|
4065
|
+
|
|
4066
|
+
/*
|
|
4067
|
+
* Copyright (c) 2024 Pobuca.
|
|
4068
|
+
* All rights reserved.
|
|
4069
|
+
*/
|
|
4070
|
+
/**
|
|
4071
|
+
* @internal
|
|
4072
|
+
*/
|
|
4073
|
+
const LIBRARY_IS_PRO = new InjectionToken('Library environment', {
|
|
4074
|
+
providedIn: 'any',
|
|
4075
|
+
factory: () => {
|
|
4076
|
+
const { xApiKey } = inject(PB_CONFIG);
|
|
4077
|
+
return !(!xApiKey || xApiKey.length < 40 || xApiKey === PB_DEFAULT_CONFIG.xApiKey);
|
|
4078
|
+
}
|
|
4079
|
+
});
|
|
4080
|
+
|
|
4081
|
+
/*
|
|
4082
|
+
* Copyright (c) 2024 Pobuca.
|
|
4083
|
+
* All rights reserved.
|
|
4084
|
+
*/
|
|
4085
|
+
/**
|
|
4086
|
+
* @internal
|
|
4087
|
+
* The free User Rest API service for Regular license.
|
|
4088
|
+
* No differences between other licenses for now, but might be in the future.
|
|
4089
|
+
*/
|
|
4090
|
+
class FreeUsersRestApi extends PbUserRestApiService {
|
|
4091
|
+
constructor(http) {
|
|
4092
|
+
super(http);
|
|
4093
|
+
}
|
|
4094
|
+
}
|
|
4095
|
+
|
|
4096
|
+
/*
|
|
4097
|
+
* Copyright (c) 2024 Pobuca.
|
|
4098
|
+
* All rights reserved.
|
|
4099
|
+
*/
|
|
4100
|
+
|
|
4101
|
+
/*
|
|
4102
|
+
* Copyright (c) 2024 Pobuca.
|
|
4103
|
+
* All rights reserved.
|
|
4104
|
+
*/
|
|
4105
|
+
/**
|
|
4106
|
+
* @internal
|
|
4107
|
+
*/
|
|
4108
|
+
class FreeUsersPbStorage extends PbStorageService {
|
|
4109
|
+
}
|
|
4110
|
+
|
|
4111
|
+
/*
|
|
4112
|
+
* Copyright (c) 2024 Pobuca.
|
|
4113
|
+
* All rights reserved.
|
|
4114
|
+
*/
|
|
4115
|
+
|
|
4116
|
+
/*
|
|
4117
|
+
* Copyright (c) 2024 Pobuca.
|
|
4118
|
+
* All rights reserved.
|
|
4119
|
+
*/
|
|
4120
|
+
/**
|
|
4121
|
+
* @internal
|
|
4122
|
+
* @param http
|
|
4123
|
+
*/
|
|
4124
|
+
function createCustomRestApiService(http) {
|
|
4125
|
+
return new FreeUsersRestApi(http);
|
|
4126
|
+
}
|
|
4127
|
+
/**
|
|
4128
|
+
* @internal
|
|
4129
|
+
* @param libraryIsProEnv
|
|
4130
|
+
*/
|
|
4131
|
+
function createUserMiddlewareService(libraryIsProEnv) {
|
|
4132
|
+
if (!libraryIsProEnv) {
|
|
4133
|
+
return new FreeUsersMiddleware();
|
|
4134
|
+
}
|
|
4135
|
+
else {
|
|
4136
|
+
return new PaidUsersMiddleware();
|
|
4137
|
+
}
|
|
4138
|
+
}
|
|
4139
|
+
/**
|
|
4140
|
+
* @internal
|
|
4141
|
+
* @param templateStorage
|
|
4142
|
+
*/
|
|
4143
|
+
function createUsersIpStorage(templateStorage) {
|
|
4144
|
+
return new FreeUsersPbStorage(templateStorage);
|
|
4145
|
+
}
|
|
4146
|
+
/**
|
|
4147
|
+
* @internal
|
|
4148
|
+
* @param libraryIsProEnv
|
|
4149
|
+
* @param matBottomSheet
|
|
4150
|
+
* @param matDialog
|
|
4151
|
+
* @param userMiddleware
|
|
4152
|
+
* @param userRestApi
|
|
4153
|
+
*/
|
|
4154
|
+
function createUserUploaderService(libraryIsProEnv, matBottomSheet, matDialog, userMiddleware, userRestApi) {
|
|
4155
|
+
if (!libraryIsProEnv) {
|
|
4156
|
+
return new ImageUploader(matBottomSheet, userMiddleware);
|
|
4157
|
+
}
|
|
4158
|
+
else {
|
|
4159
|
+
return new PaidUsersImageUploaderServiceService(matDialog, userMiddleware, userRestApi);
|
|
4160
|
+
}
|
|
4161
|
+
}
|
|
4162
|
+
const ɵ0 = {
|
|
4163
|
+
bounds: 'pb-builder-container',
|
|
4164
|
+
theme: 'bubble',
|
|
4165
|
+
format: 'html',
|
|
4166
|
+
trackChanges: 'all',
|
|
4167
|
+
suppressGlobalRegisterWarning: true
|
|
4168
|
+
};
|
|
4169
|
+
class PbEmailBuilderModule {
|
|
4170
|
+
constructor(pbMiddlewaresService, userImageUploader, libraryIsPro) {
|
|
4171
|
+
if (!libraryIsPro && !(pbMiddlewaresService instanceof FreeUsersMiddleware)) {
|
|
4172
|
+
throw new Error('Upgrade to an Extended or Commercial License to use Custom Middlewares.');
|
|
4173
|
+
}
|
|
4174
|
+
if (!libraryIsPro && !(userImageUploader instanceof ImageUploader)) {
|
|
4175
|
+
throw new Error('Upgrade to an Extended or Commercial License to use Custom Image Uploader.');
|
|
4176
|
+
}
|
|
4177
|
+
}
|
|
4178
|
+
/**
|
|
4179
|
+
* @deprecated use PbEmailBuilderModule.withConfig(userConfig) instead
|
|
4180
|
+
* @internal
|
|
4181
|
+
*/
|
|
4182
|
+
static forRoot(userConfig = {}) {
|
|
4183
|
+
return PbEmailBuilderModule.withConfig(userConfig);
|
|
4184
|
+
}
|
|
4185
|
+
/**
|
|
4186
|
+
* Import module with configurations
|
|
4187
|
+
* @param userConfig Configurations
|
|
4188
|
+
*/
|
|
4189
|
+
static withConfig(userConfig = {}) {
|
|
4190
|
+
return {
|
|
4191
|
+
ngModule: PbEmailBuilderModule,
|
|
4192
|
+
providers: [
|
|
4193
|
+
PbEmailBuilderService,
|
|
4194
|
+
PbEmailObjectStoreService,
|
|
4195
|
+
PbUserInterfaceService,
|
|
4196
|
+
{ provide: PB_CONFIG, useValue: userConfig },
|
|
4197
|
+
{
|
|
4198
|
+
provide: PbUserMiddlewaresService,
|
|
4199
|
+
useFactory: createUserMiddlewareService,
|
|
4200
|
+
deps: [LIBRARY_IS_PRO]
|
|
4201
|
+
},
|
|
4202
|
+
{
|
|
4203
|
+
provide: PbStorageService,
|
|
4204
|
+
useFactory: createUsersIpStorage,
|
|
4205
|
+
deps: [PB_TEMPLATES_TEMPORARY_STORAGE]
|
|
4206
|
+
},
|
|
4207
|
+
{
|
|
4208
|
+
provide: PbUserImageUploaderService,
|
|
4209
|
+
useFactory: createUserUploaderService,
|
|
4210
|
+
deps: [LIBRARY_IS_PRO, MatBottomSheet, MatDialog, PbUserMiddlewaresService, PbUserRestApiService]
|
|
4211
|
+
},
|
|
4212
|
+
{
|
|
4213
|
+
provide: PbUserRestApiService,
|
|
4214
|
+
useFactory: createCustomRestApiService,
|
|
4215
|
+
deps: [HttpClient]
|
|
4216
|
+
}
|
|
4217
|
+
]
|
|
4218
|
+
};
|
|
4219
|
+
}
|
|
4220
|
+
}
|
|
4221
|
+
PbEmailBuilderModule.decorators = [
|
|
4222
|
+
{ type: NgModule, args: [{
|
|
4223
|
+
imports: [
|
|
4224
|
+
CommonModule,
|
|
4225
|
+
HttpClientModule,
|
|
4226
|
+
QuillModule,
|
|
4227
|
+
FlexLayoutModule,
|
|
4228
|
+
DragDropModule,
|
|
4229
|
+
FormsModule,
|
|
4230
|
+
ColorChromeModule,
|
|
4231
|
+
MatButtonModule,
|
|
4232
|
+
MatIconModule,
|
|
4233
|
+
MatFormFieldModule,
|
|
4234
|
+
MatInputModule,
|
|
4235
|
+
MatTabsModule,
|
|
4236
|
+
MatSliderModule,
|
|
4237
|
+
MatSelectModule,
|
|
4238
|
+
MatSlideToggleModule,
|
|
4239
|
+
MatButtonToggleModule,
|
|
4240
|
+
MatDividerModule,
|
|
4241
|
+
MatTooltipModule,
|
|
4242
|
+
MatDialogModule,
|
|
4243
|
+
MatSnackBarModule,
|
|
4244
|
+
MatProgressBarModule,
|
|
4245
|
+
MatMenuModule,
|
|
4246
|
+
MatSidenavModule,
|
|
4247
|
+
MatToolbarModule,
|
|
4248
|
+
MatListModule,
|
|
4249
|
+
MatBottomSheetModule,
|
|
4250
|
+
MatDividerModule,
|
|
4251
|
+
MatExpansionModule,
|
|
4252
|
+
ResizableModule,
|
|
4253
|
+
ScrollingModule,
|
|
4254
|
+
PortalModule
|
|
4255
|
+
],
|
|
4256
|
+
declarations: [
|
|
4257
|
+
PbEmailBuilderComponent,
|
|
4258
|
+
StructureComponent,
|
|
4259
|
+
BlockComponent,
|
|
4260
|
+
DynamicComponentDirective,
|
|
4261
|
+
PreviewTemplateComponent,
|
|
4262
|
+
TextElementComponent,
|
|
4263
|
+
ImageComponent,
|
|
4264
|
+
ButtonComponent,
|
|
4265
|
+
DividerComponent,
|
|
4266
|
+
SpacerComponent,
|
|
4267
|
+
SocialComponent,
|
|
4268
|
+
BuilderContainerComponent,
|
|
4269
|
+
FontStylesComponent,
|
|
4270
|
+
PaddingComponent,
|
|
4271
|
+
MarginComponent,
|
|
4272
|
+
GapsComponent,
|
|
4273
|
+
LineHeightComponent,
|
|
4274
|
+
WidthHeightComponent,
|
|
4275
|
+
BorderComponent,
|
|
4276
|
+
ColorComponent,
|
|
4277
|
+
LinkComponent,
|
|
4278
|
+
AlignComponent,
|
|
4279
|
+
DirectionComponent,
|
|
4280
|
+
BackRepatComponent,
|
|
4281
|
+
ConfirmDialogComponent,
|
|
4282
|
+
ImageUploadComponent,
|
|
4283
|
+
BlockSettingsComponent,
|
|
4284
|
+
StructureSettingsComponent,
|
|
4285
|
+
GeneralSettingsComponent,
|
|
4286
|
+
UploadBottomSheetDialogComponent,
|
|
4287
|
+
UploadImageGalleryComponent,
|
|
4288
|
+
TemplateListDialogComponent,
|
|
4289
|
+
SlugifyPipe,
|
|
4290
|
+
ImportDialogComponent
|
|
4291
|
+
],
|
|
4292
|
+
exports: [PbEmailBuilderComponent, PreviewTemplateComponent, SlugifyPipe],
|
|
4293
|
+
entryComponents: [
|
|
4294
|
+
TextElementComponent,
|
|
4295
|
+
ImageComponent,
|
|
4296
|
+
ButtonComponent,
|
|
4297
|
+
DividerComponent,
|
|
4298
|
+
SpacerComponent,
|
|
4299
|
+
SocialComponent,
|
|
4300
|
+
ConfirmDialogComponent,
|
|
4301
|
+
UploadBottomSheetDialogComponent,
|
|
4302
|
+
UploadImageGalleryComponent,
|
|
4303
|
+
TemplateListDialogComponent,
|
|
4304
|
+
ImportDialogComponent
|
|
4305
|
+
],
|
|
4306
|
+
providers: [
|
|
4307
|
+
{
|
|
4308
|
+
provide: QUILL_CONFIG_TOKEN,
|
|
4309
|
+
useValue: ɵ0
|
|
4310
|
+
},
|
|
4311
|
+
{ provide: HTTP_INTERCEPTORS, useClass: IpInterceptorInterceptor, multi: true },
|
|
4312
|
+
// { provide: PB_BLOCK_ELEMENT, useClass: TextBlock, multi: true },
|
|
4313
|
+
PbEmailBuilderService,
|
|
4314
|
+
PbEmailObjectStoreService,
|
|
4315
|
+
PbUserInterfaceService
|
|
4316
|
+
]
|
|
4317
|
+
},] }
|
|
4318
|
+
];
|
|
4319
|
+
PbEmailBuilderModule.ctorParameters = () => [
|
|
4320
|
+
{ type: PbUserMiddlewaresService },
|
|
4321
|
+
{ type: PbUserImageUploaderService },
|
|
4322
|
+
{ type: Boolean, decorators: [{ type: Inject, args: [LIBRARY_IS_PRO,] }] }
|
|
4323
|
+
];
|
|
4324
|
+
|
|
4325
|
+
/*
|
|
4326
|
+
* Copyright (c) 2024 Pobuca.
|
|
4327
|
+
* All rights reserved.
|
|
4328
|
+
*/
|
|
4329
|
+
|
|
4330
|
+
/**
|
|
4331
|
+
* Generated bundle index. Do not edit.
|
|
4332
|
+
*/
|
|
4333
|
+
|
|
4334
|
+
export { AIPBlock, ButtonBlock, Defaults, DividerBlock, ETemplatesStorage, ImageUploader as FreeUsersImageUploader, ImageBlock, PBEmail, PB_BLOCKS, PB_CONFIG, PB_DEFAULT_BLOCKS, PB_DEFAULT_CONFIG, PB_DEFAULT_FALLBACK_FONTS, PB_DEFAULT_GOOGLE_FONTS, PB_DEFAULT_SOCIAL_NETWORKS, PB_DEFAULT_STRUCTURES, PB_FALLBACK_FONTS, PB_GOOGLE_FONTS, PB_SOCIAL_NETWORKS, PB_STORAGE_FACTORY, PB_STRUCTURES, PB_TEMPLATES_TEMPORARY_STORAGE, PbEmailBuilderComponent, PbEmailBuilderModule, PbEmailBuilderService, PbStorageService, PbUserImageUploaderService, PbUserMiddlewaresService, PbUserRestApiService, PreviewTemplateComponent, SlugifyPipe, SocialBlock, SpacerBlock, Structure, TextBlock, bytesToSize, cloneDeep, createBackground, createBorder, createCustomRestApiService, createFont, createLineHeight, createMargin, createPadding, createUserMiddlewareService, createUserUploaderService, createUsersIpStorage, createWidthHeight, defaultColumnsOptions, defaultsDeep, deferOf, getAssetByPath, isEqual, ɵ0, PbEmailObjectStoreService as ɵa, PbUserInterfaceService as ɵb, BackRepatComponent as ɵba, ConfirmDialogComponent as ɵbb, ImageUploadComponent as ɵbc, BlockSettingsComponent as ɵbd, StructureSettingsComponent as ɵbe, GeneralSettingsComponent as ɵbf, UploadBottomSheetDialogComponent as ɵbg, UploadImageGalleryComponent as ɵbh, TemplateListDialogComponent as ɵbi, ImportDialogComponent as ɵbj, IpInterceptorInterceptor as ɵbk, LIBRARY_IS_PRO as ɵbl, FreeUsersRestApi as ɵc, FreeUsersPbStorage as ɵd, StructureComponent as ɵe, BlockComponent as ɵf, DynamicComponentDirective as ɵg, TextElementComponent as ɵh, AbstractBlock as ɵi, ImageComponent as ɵj, ButtonComponent as ɵk, DividerComponent as ɵl, SpacerComponent as ɵm, SocialComponent as ɵn, BuilderContainerComponent as ɵo, FontStylesComponent as ɵp, PaddingComponent as ɵq, MarginComponent as ɵr, GapsComponent as ɵs, LineHeightComponent as ɵt, WidthHeightComponent as ɵu, BorderComponent as ɵv, ColorComponent as ɵw, LinkComponent as ɵx, AlignComponent as ɵy, DirectionComponent as ɵz };
|
|
4335
|
+
//# sourceMappingURL=pobuca-email-builder.js.map
|