@bnsights/bbsf-controls 1.2.27 → 1.2.29
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 +69 -46
- package/fesm2022/bnsights-bbsf-controls.mjs +121 -38
- package/fesm2022/bnsights-bbsf-controls.mjs.map +1 -1
- package/lib/Shared/Models/ControlOptionsBase.d.ts +46 -0
- package/lib/Shared/Models/FileUploadCropperOptions.d.ts +4 -0
- package/lib/Shared/Models/FileUploadOptions.d.ts +14 -0
- package/lib/Shared/services/ControlUtility.d.ts +2 -1
- package/lib/Shared/services/validationErrorMassage.service.d.ts +2 -1
- package/lib/controls/LogoCropper/LogoCropper.component.d.ts +18 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -327,19 +327,19 @@ import { BBSFFormsBasicModule, BBSFUploadsModule } from '@bnsights/bbsf-controls
|
|
|
327
327
|
|
|
328
328
|
---
|
|
329
329
|
|
|
330
|
-
## 🔧 Troubleshooting
|
|
331
|
-
|
|
332
|
-
### Markdown editor assets and behavior
|
|
333
|
-
|
|
334
|
-
`BBSF-MarkdownEditor` is now powered by Tiptap and no longer depends on the previous third-party markdown editor, Ace, runtime markdown scripts, or copied markdown editor assets.
|
|
335
|
-
|
|
336
|
-
The control keeps the BBSF API and form integration, but the old split preview panel, `customRender`, and built-in image upload behavior are no longer provided by the editor engine.
|
|
337
|
-
|
|
338
|
-
---
|
|
339
|
-
|
|
340
|
-
### Error: `export 'DOCUMENT' (imported as 'DOCUMENT') was not found in '@angular/core'`
|
|
341
|
-
|
|
342
|
-
**Problem:** This error occurs when using Angular 19+ with the `BBSFEditorsModule` (which uses `@kolkov/angular-editor`). Angular 19's build system tries to process the `@kolkov/angular-editor` package and encounters compatibility issues.
|
|
330
|
+
## 🔧 Troubleshooting
|
|
331
|
+
|
|
332
|
+
### Markdown editor assets and behavior
|
|
333
|
+
|
|
334
|
+
`BBSF-MarkdownEditor` is now powered by Tiptap and no longer depends on the previous third-party markdown editor, Ace, runtime markdown scripts, or copied markdown editor assets.
|
|
335
|
+
|
|
336
|
+
The control keeps the BBSF API and form integration, but the old split preview panel, `customRender`, and built-in image upload behavior are no longer provided by the editor engine.
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
### Error: `export 'DOCUMENT' (imported as 'DOCUMENT') was not found in '@angular/core'`
|
|
341
|
+
|
|
342
|
+
**Problem:** This error occurs when using Angular 19+ with the `BBSFEditorsModule` (which uses `@kolkov/angular-editor`). Angular 19's build system tries to process the `@kolkov/angular-editor` package and encounters compatibility issues.
|
|
343
343
|
|
|
344
344
|
**Solution 1: Install @kolkov/angular-editor in your consuming app (Recommended)**
|
|
345
345
|
|
|
@@ -398,38 +398,61 @@ If the issue persists, consider using a different HTML editor component that's c
|
|
|
398
398
|
|
|
399
399
|
---
|
|
400
400
|
|
|
401
|
-
## Change Log
|
|
402
|
-
|
|
403
|
-
## 1.2.
|
|
404
|
-
|
|
405
|
-
=====================
|
|
406
|
-
|
|
407
|
-
- Added
|
|
408
|
-
- Added
|
|
409
|
-
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
- Added
|
|
416
|
-
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
401
|
+
## Change Log
|
|
402
|
+
|
|
403
|
+
## 1.2.29 / 05-07-2026
|
|
404
|
+
|
|
405
|
+
=====================
|
|
406
|
+
|
|
407
|
+
- Added `allowMoveImage` support to `LogoCropperComponent` to pan the source image under a static crop box, with the CSS pointer-events fix (`.ngx-ic-cropper { pointer-events: none; }`) required to let drags reach the image instead of the crop-box overlay.
|
|
408
|
+
- Added `containWithinAspectRatio` support; padding is now computed from an `effectiveAspectRatio` getter so it matches the static crop box shape when one is set, instead of a mismatched `aspectRatio` input.
|
|
409
|
+
- Added 90° rotate left/right buttons using `canvasRotation` (does not clip corners like `transform.rotate`).
|
|
410
|
+
- Added a full reset button (zoom, pan, and rotation), replacing the previous zoom-only reset.
|
|
411
|
+
- Added mouse-wheel zoom on the cropper.
|
|
412
|
+
- Added `loadImageFailed` handling: logs the error, shows a toast, and dismisses the modal so the caller can clear the upload.
|
|
413
|
+
- Added optional display scaling (`maxDisplayWidth`/`maxDisplayHeight`, default `0` = disabled) so small target crop sizes can be shown enlarged on screen while the exported file is resized back to the true target dimensions.
|
|
414
|
+
- Extended `FileUploadCropperOptions` with `allowMoveImage`, `containWithinAspectRatio`, `maxDisplayWidth`, and `maxDisplayHeight`, threaded through `BBSF-FileUpload`'s cropper modal.
|
|
415
|
+
- Added `ResetZoom` and `ImageLoadFailed` resource keys.
|
|
416
|
+
- All additions are backward compatible; existing consumers are unaffected until they opt in.
|
|
417
|
+
|
|
418
|
+
## 1.2.28 / 23-06-2026
|
|
419
|
+
|
|
420
|
+
=====================
|
|
421
|
+
|
|
422
|
+
- Added optional `FileUploadOptions` overrides for validation error message labels.
|
|
423
|
+
- Supported host-provided localized labels for max file count, min file count, total size, file size, type tooltip, duplicate file, and required validation messages.
|
|
424
|
+
- Threaded file upload options through validation helpers and applied the duplicate-file override message.
|
|
425
|
+
|
|
426
|
+
## 1.2.27 / 22-06-2026
|
|
427
|
+
|
|
428
|
+
=====================
|
|
429
|
+
|
|
430
|
+
- Added optional image cropper support to `BBSF-FileUpload` through `FileUploadOptions.cropperOptions`.
|
|
431
|
+
- Added exported cropper option models for configuring modal behavior, orientation presets, and cancel behavior.
|
|
432
|
+
- Preserved existing upload behavior unless cropper options are explicitly enabled.
|
|
433
|
+
|
|
434
|
+
## 1.2.26 / 15-06-2026
|
|
435
|
+
|
|
436
|
+
=====================
|
|
437
|
+
|
|
438
|
+
- Added explicit package exports for controls SCSS assets so Angular browser and application builders can resolve deep style imports from consuming packages.
|
|
439
|
+
- Kept backward compatibility for extensionless Sass imports such as `@bnsights/bbsf-controls/src/lib/assets/sass/variables`.
|
|
440
|
+
- Marked CSS, SCSS, and packaged assets as side-effectful so published global styles are preserved by consumers.
|
|
441
|
+
- Added `LogoCropperComponent` as a standalone upload modal component exported from `@bnsights/bbsf-controls`.
|
|
442
|
+
- Re-exported `LogoCropperComponent` through `BBSFUploadsModule` and the package public API.
|
|
443
|
+
- Added `ngx-image-cropper` dependency for logo cropping support with Angular 19-compatible peer dependencies.
|
|
444
|
+
|
|
445
|
+
## 1.2.25 / 15-06-2026
|
|
446
|
+
|
|
447
|
+
=====================
|
|
448
|
+
|
|
449
|
+
- Replaced the BBSF MarkdownEditor engine with a Tiptap-powered markdown editor while keeping the existing `BBSF-MarkdownEditor` selector, options model, form integration, validation, and `BBSFEditorsModule` export.
|
|
450
|
+
- Removed the old markdown editor runtime asset dependency path, including Ace and copied markdown editor assets.
|
|
451
|
+
- Added Tiptap and PrimeIcons dependencies for the new MarkdownEditor toolbar and markdown serialization.
|
|
452
|
+
|
|
453
|
+
## 1.2.24 / 14-06-2026
|
|
454
|
+
|
|
455
|
+
=====================
|
|
433
456
|
|
|
434
457
|
- Add new file-upload props for override labels
|
|
435
458
|
{
|
|
@@ -456,7 +479,7 @@ If the issue persists, consider using a different HTML editor component that's c
|
|
|
456
479
|
- Fixed package stylesheet imports for Angular esbuild application builder compatibility.
|
|
457
480
|
- Replaced webpack-era `/node_modules/...` SCSS imports in `src/lib/assets/Style.scss` with relative package imports.
|
|
458
481
|
- Removed `.css` extensions from SCSS imports so Sass resolves and inlines vendor styles instead of leaving CSS `@import` statements for esbuild.
|
|
459
|
-
- Fixed imports for Angular Material prebuilt theme, ngx-toastr, intl-tel-input, markdown editor styles, and angular-datetime-picker styles.
|
|
482
|
+
- Fixed imports for Angular Material prebuilt theme, ngx-toastr, intl-tel-input, markdown editor styles, and angular-datetime-picker styles.
|
|
460
483
|
|
|
461
484
|
## 1.2.22 / 06-05-2026
|
|
462
485
|
|
|
@@ -106,95 +106,95 @@ class ErrorMassageValidation {
|
|
|
106
106
|
control.setErrors({ minlength: true });
|
|
107
107
|
control.markAsTouched();
|
|
108
108
|
}
|
|
109
|
-
getErrorMassageValidation(error) {
|
|
109
|
+
getErrorMassageValidation(error, options) {
|
|
110
110
|
let result = '';
|
|
111
111
|
switch (error.key) {
|
|
112
112
|
case 'required':
|
|
113
|
-
result = this.utilityService.getResourceValue('RequiredValidationKey');
|
|
113
|
+
result = options?.requiredErrorMsg || this.utilityService.getResourceValue('RequiredValidationKey');
|
|
114
114
|
break;
|
|
115
115
|
case 'email':
|
|
116
|
-
result = this.utilityService.getResourceValue('EmailValidationKey');
|
|
116
|
+
result = options?.emailErrorMsg || this.utilityService.getResourceValue('EmailValidationKey');
|
|
117
117
|
break;
|
|
118
118
|
case 'EmailValidationKey':
|
|
119
|
-
result = this.utilityService.getResourceValue('EmailValidationKey');
|
|
119
|
+
result = options?.emailErrorMsg || this.utilityService.getResourceValue('EmailValidationKey');
|
|
120
120
|
break;
|
|
121
121
|
case 'URLValidationKey':
|
|
122
|
-
result = this.utilityService.getResourceValue('urlValidationError');
|
|
122
|
+
result = options?.urlErrorMsg || this.utilityService.getResourceValue('urlValidationError');
|
|
123
123
|
break;
|
|
124
124
|
case 'pattern':
|
|
125
|
-
result = this.utilityService.getResourceValue('NumberValidationKey');
|
|
125
|
+
result = options?.numberErrorMsg || this.utilityService.getResourceValue('NumberValidationKey');
|
|
126
126
|
break;
|
|
127
127
|
case 'maxlength':
|
|
128
|
-
result = `${this.utilityService.getResourceValue('MaxLenghtValidationKey')} ${error.value.requiredLength}`;
|
|
128
|
+
result = `${options?.maxLengthErrorMsg || this.utilityService.getResourceValue('MaxLenghtValidationKey')} ${error.value.requiredLength}`;
|
|
129
129
|
break;
|
|
130
130
|
case 'minlength':
|
|
131
|
-
result = `${this.utilityService.getResourceValue('MinLenghtValidationKey')} ${error.value.requiredLength}`;
|
|
131
|
+
result = `${options?.minLengthErrorMsg || this.utilityService.getResourceValue('MinLenghtValidationKey')} ${error.value.requiredLength}`;
|
|
132
132
|
break;
|
|
133
133
|
case 'min':
|
|
134
|
-
result = `${this.utilityService.getResourceValue('MinValueValidationKey')} ${error.value.min}`;
|
|
134
|
+
result = `${options?.minValueErrorMsg || this.utilityService.getResourceValue('MinValueValidationKey')} ${error.value.min}`;
|
|
135
135
|
break;
|
|
136
136
|
case 'max':
|
|
137
|
-
result = `${this.utilityService.getResourceValue('MaxValueValidationKey')} ${error.value.max}`;
|
|
137
|
+
result = `${options?.maxValueErrorMsg || this.utilityService.getResourceValue('MaxValueValidationKey')} ${error.value.max}`;
|
|
138
138
|
break;
|
|
139
139
|
case 'IntegerNumberValidationKey':
|
|
140
|
-
result = this.utilityService.getResourceValue('IntegerNumberValidationKey');
|
|
140
|
+
result = options?.integerNumberErrorMsg || this.utilityService.getResourceValue('IntegerNumberValidationKey');
|
|
141
141
|
break;
|
|
142
142
|
case 'PositiveNumberValidationKey':
|
|
143
|
-
result = this.utilityService.getResourceValue('PositiveNumberValidationKey');
|
|
143
|
+
result = options?.positiveNumberErrorMsg || this.utilityService.getResourceValue('PositiveNumberValidationKey');
|
|
144
144
|
break;
|
|
145
145
|
case 'validatePhoneNumber':
|
|
146
|
-
result = this.utilityService.getResourceValue('PhoneNumberValidationKey');
|
|
146
|
+
result = options?.phoneNumberErrorMsg || this.utilityService.getResourceValue('PhoneNumberValidationKey');
|
|
147
147
|
break;
|
|
148
148
|
case 'mask':
|
|
149
|
-
result = `${this.utilityService.getResourceValue('MaskValidationKey')} ${error.value.requiredMask}`;
|
|
149
|
+
result = `${options?.maskErrorMsg || this.utilityService.getResourceValue('MaskValidationKey')} ${error.value.requiredMask}`;
|
|
150
150
|
break;
|
|
151
151
|
case 'passwordComplexityHasNumber':
|
|
152
|
-
result = this.utilityService.getResourceValue('PasswordComplexityHasNumber');
|
|
152
|
+
result = options?.passwordHasNumberErrorMsg || this.utilityService.getResourceValue('PasswordComplexityHasNumber');
|
|
153
153
|
break;
|
|
154
154
|
case 'ArabicLetterOnly':
|
|
155
|
-
result = this.utilityService.getResourceValue('ArabicLetterOnly');
|
|
155
|
+
result = options?.arabicLetterOnlyErrorMsg || this.utilityService.getResourceValue('ArabicLetterOnly');
|
|
156
156
|
break;
|
|
157
157
|
case 'EnglishLetterOnly':
|
|
158
|
-
result = this.utilityService.getResourceValue('EnglishLetterOnly');
|
|
158
|
+
result = options?.englishLetterOnlyErrorMsg || this.utilityService.getResourceValue('EnglishLetterOnly');
|
|
159
159
|
break;
|
|
160
160
|
case 'ArabicIsRequiredAndOnly50CharactersEnglish':
|
|
161
|
-
result = this.utilityService.getResourceValue('VAL_ArabicIsRequiredAndOnly50CharactersEnglish');
|
|
161
|
+
result = options?.arabicRequiredAndEnglishMax50ErrorMsg || this.utilityService.getResourceValue('VAL_ArabicIsRequiredAndOnly50CharactersEnglish');
|
|
162
162
|
break;
|
|
163
163
|
case 'passwordComplexityHasCapitalLetter':
|
|
164
|
-
result = this.utilityService.getResourceValue('PasswordComplexityHasCapitalLetter');
|
|
164
|
+
result = options?.passwordHasCapitalLetterErrorMsg || this.utilityService.getResourceValue('PasswordComplexityHasCapitalLetter');
|
|
165
165
|
break;
|
|
166
166
|
case 'passwordComplexityHasSmallLetter':
|
|
167
|
-
result = this.utilityService.getResourceValue('PasswordComplexityHasSmallLetter');
|
|
167
|
+
result = options?.passwordHasSmallLetterErrorMsg || this.utilityService.getResourceValue('PasswordComplexityHasSmallLetter');
|
|
168
168
|
break;
|
|
169
169
|
case 'passwordComplexityHasSpecialLetter':
|
|
170
|
-
result = this.utilityService.getResourceValue('PasswordComplexityHasSpecialLetter');
|
|
170
|
+
result = options?.passwordHasSpecialLetterErrorMsg || this.utilityService.getResourceValue('PasswordComplexityHasSpecialLetter');
|
|
171
171
|
break;
|
|
172
172
|
case 'MaxWordCountValidationKey':
|
|
173
|
-
result = `${this.utilityService.getResourceValue('MaxWordCountValidationKey')} ${error.value}`;
|
|
173
|
+
result = `${options?.maxWordCountErrorMsg || this.utilityService.getResourceValue('MaxWordCountValidationKey')} ${error.value}`;
|
|
174
174
|
break;
|
|
175
175
|
case 'NewSelectionValidationKey':
|
|
176
|
-
result = this.utilityService.getResourceValue('NewSelectionValidationKey');
|
|
176
|
+
result = options?.newSelectionErrorMsg || this.utilityService.getResourceValue('NewSelectionValidationKey');
|
|
177
177
|
break;
|
|
178
178
|
case 'MaxFileCountValidationKey':
|
|
179
|
-
result = `${this.utilityService.getResourceValue('MaxFileCountValidationKey')} ${error.value}`;
|
|
179
|
+
result = `${options?.maxFileCountErrorMsg || this.utilityService.getResourceValue('MaxFileCountValidationKey')} ${error.value}`;
|
|
180
180
|
break;
|
|
181
181
|
case 'MinFileCountValidationKey':
|
|
182
|
-
result = `${this.utilityService.getResourceValue('MinFileCountValidationKey')} ${error.value}`;
|
|
182
|
+
result = `${options?.minFileCountErrorMsg || this.utilityService.getResourceValue('MinFileCountValidationKey')} ${error.value}`;
|
|
183
183
|
break;
|
|
184
184
|
case 'MaxSizeForAllFilesInMB':
|
|
185
|
-
result = `${this.utilityService.getResourceValue('MaxSizeForAllFilesInMB')} ${error.value}`;
|
|
185
|
+
result = `${options?.maxSizeTotalErrorMsg || this.utilityService.getResourceValue('MaxSizeForAllFilesInMB')} ${error.value}`;
|
|
186
186
|
break;
|
|
187
187
|
case 'FileMaxSizeInMB':
|
|
188
|
-
result = `${this.utilityService.getResourceValue('FileMaxSizeInMB')} ${error.value}`;
|
|
188
|
+
result = `${options?.maxFileSizeErrorMsg || this.utilityService.getResourceValue('FileMaxSizeInMB')} ${error.value}`;
|
|
189
189
|
break;
|
|
190
190
|
case 'ToolTipTypeError':
|
|
191
|
-
result = `${this.utilityService.getResourceValue('ToolTipTypeError')} ${error.value}`;
|
|
191
|
+
result = `${options?.toolTipTypeErrorMsg || this.utilityService.getResourceValue('ToolTipTypeError')} ${error.value}`;
|
|
192
192
|
break;
|
|
193
193
|
case 'InvalidEndDate':
|
|
194
|
-
result = this.utilityService.getResourceValue('InvalidEndDate');
|
|
194
|
+
result = options?.invalidEndDateErrorMsg || this.utilityService.getResourceValue('InvalidEndDate');
|
|
195
195
|
break;
|
|
196
196
|
case 'InvalidStartDate':
|
|
197
|
-
result = this.utilityService.getResourceValue('InvalidStartDate');
|
|
197
|
+
result = options?.invalidStartDateErrorMsg || this.utilityService.getResourceValue('InvalidStartDate');
|
|
198
198
|
break;
|
|
199
199
|
case 'DuplicateFileError':
|
|
200
200
|
result = error.value;
|
|
@@ -219,7 +219,7 @@ class ControlUtility {
|
|
|
219
219
|
this.controlValidationService = controlValidationService;
|
|
220
220
|
this.utilityService = utilityService;
|
|
221
221
|
}
|
|
222
|
-
getErrorValidation(ErrorsItem, customValidation) {
|
|
222
|
+
getErrorValidation(ErrorsItem, customValidation, options) {
|
|
223
223
|
let result = '';
|
|
224
224
|
ErrorsItem.forEach(element => {
|
|
225
225
|
if (element.key === 'customerError' && ErrorsItem.length === 1) {
|
|
@@ -235,7 +235,7 @@ class ControlUtility {
|
|
|
235
235
|
result = element.value;
|
|
236
236
|
}
|
|
237
237
|
else {
|
|
238
|
-
result = this.ErrorHandler.getErrorMassageValidation(element);
|
|
238
|
+
result = this.ErrorHandler.getErrorMassageValidation(element, options);
|
|
239
239
|
}
|
|
240
240
|
});
|
|
241
241
|
return result;
|
|
@@ -305,7 +305,7 @@ class ControlUtility {
|
|
|
305
305
|
// UtilityService.showGlobalError();
|
|
306
306
|
// this.scrollToGlobalErrorMassage()
|
|
307
307
|
// }
|
|
308
|
-
return this.getErrorValidation(ErrorList, options.customValidation);
|
|
308
|
+
return this.getErrorValidation(ErrorList, options.customValidation, options);
|
|
309
309
|
}
|
|
310
310
|
scrollToGlobalErrorMassage() {
|
|
311
311
|
let element = document.getElementById('errorId');
|
|
@@ -2568,6 +2568,14 @@ class FileUploadCropperOptions {
|
|
|
2568
2568
|
this.backdrop = 'static';
|
|
2569
2569
|
this.maintainAspectRatio = true;
|
|
2570
2570
|
this.onlyScaleDown = true;
|
|
2571
|
+
this.allowMoveImage = false;
|
|
2572
|
+
this.containWithinAspectRatio = false;
|
|
2573
|
+
// Maximum on-screen size of the crop box; 0 keeps the crop box at its true
|
|
2574
|
+
// (output) size. When set, the output file is still resized back to the
|
|
2575
|
+
// preset's static dimensions. Pick values that fit the modal size (an
|
|
2576
|
+
// ng-bootstrap 'lg' modal body is ~770px wide).
|
|
2577
|
+
this.maxDisplayWidth = 0;
|
|
2578
|
+
this.maxDisplayHeight = 0;
|
|
2571
2579
|
this.cancelBehavior = 'clear';
|
|
2572
2580
|
this.horizontal = new FileUploadCropperPreset(3 / 1, 90, 45);
|
|
2573
2581
|
this.vertical = new FileUploadCropperPreset(3 / 2, 200, 66);
|
|
@@ -7352,28 +7360,91 @@ class LogoCropperComponent {
|
|
|
7352
7360
|
this.cropperStaticWidth = 0;
|
|
7353
7361
|
this.cropperStaticHeight = 0;
|
|
7354
7362
|
this.onlyScaleDown = true;
|
|
7355
|
-
this.
|
|
7363
|
+
this.allowMoveImage = false;
|
|
7364
|
+
this.containWithinAspectRatio = false;
|
|
7365
|
+
// Maximum on-screen size of the crop box; 0 disables display scaling and
|
|
7366
|
+
// keeps the crop box at its true (output) size.
|
|
7367
|
+
this.maxDisplayWidth = 0;
|
|
7368
|
+
this.maxDisplayHeight = 0;
|
|
7369
|
+
this.transform = { scale: 1, translateH: 0, translateV: 0, translateUnit: 'px' };
|
|
7370
|
+
this.canvasRotation = 0;
|
|
7356
7371
|
this.croppedBlob = null;
|
|
7357
7372
|
this.minScale = 1;
|
|
7358
7373
|
this.maxScale = 4;
|
|
7359
7374
|
this.scaleStep = 0.1;
|
|
7360
7375
|
}
|
|
7361
7376
|
ngOnInit() { }
|
|
7377
|
+
get displayScalingEnabled() {
|
|
7378
|
+
return this.maxDisplayWidth > 0 && this.maxDisplayHeight > 0 &&
|
|
7379
|
+
this.cropperStaticWidth > 0 && this.cropperStaticHeight > 0;
|
|
7380
|
+
}
|
|
7381
|
+
// Scale the crop box up so the user can see the image clearly while cropping,
|
|
7382
|
+
// while the uploaded file is resized back to the target dimensions.
|
|
7383
|
+
get displayScale() {
|
|
7384
|
+
if (!this.displayScalingEnabled)
|
|
7385
|
+
return 1;
|
|
7386
|
+
const scale = Math.min(this.maxDisplayWidth / this.cropperStaticWidth, this.maxDisplayHeight / this.cropperStaticHeight);
|
|
7387
|
+
return Math.max(1, scale);
|
|
7388
|
+
}
|
|
7389
|
+
get displayWidth() {
|
|
7390
|
+
return Math.round(this.cropperStaticWidth * this.displayScale);
|
|
7391
|
+
}
|
|
7392
|
+
get displayHeight() {
|
|
7393
|
+
return Math.round(this.cropperStaticHeight * this.displayScale);
|
|
7394
|
+
}
|
|
7395
|
+
// The contain-within-aspect-ratio padding is computed from the aspectRatio
|
|
7396
|
+
// option, so it must match the static crop box shape when one is set.
|
|
7397
|
+
get effectiveAspectRatio() {
|
|
7398
|
+
if (this.cropperStaticWidth && this.cropperStaticHeight) {
|
|
7399
|
+
return this.cropperStaticWidth / this.cropperStaticHeight;
|
|
7400
|
+
}
|
|
7401
|
+
return this.aspectRatio;
|
|
7402
|
+
}
|
|
7403
|
+
// Source-image size caps consumed by the component scss; null falls back to
|
|
7404
|
+
// the legacy fixed caps so display scaling stays opt-in.
|
|
7405
|
+
get sourceMaxHeight() {
|
|
7406
|
+
return this.displayScalingEnabled ? `${this.displayHeight}px` : null;
|
|
7407
|
+
}
|
|
7408
|
+
get sourceMaxWidth() {
|
|
7409
|
+
return this.displayScalingEnabled ? `${this.displayWidth}px` : null;
|
|
7410
|
+
}
|
|
7362
7411
|
translate(key) {
|
|
7363
7412
|
return this.utilityService.getResourceValue(key);
|
|
7364
7413
|
}
|
|
7365
7414
|
imageCropped(event) {
|
|
7366
7415
|
this.croppedBlob = event.blob ?? null;
|
|
7367
7416
|
}
|
|
7417
|
+
imageLoadFailed(error) {
|
|
7418
|
+
console.error('Logo cropper failed to load image', error);
|
|
7419
|
+
this.utilityService.notifyErrorMessage(this.translate('ImageLoadFailed'));
|
|
7420
|
+
this.activeModal.dismiss();
|
|
7421
|
+
}
|
|
7422
|
+
onWheel(event) {
|
|
7423
|
+
event.preventDefault();
|
|
7424
|
+
if (event.deltaY < 0)
|
|
7425
|
+
this.zoomIn();
|
|
7426
|
+
else if (event.deltaY > 0)
|
|
7427
|
+
this.zoomOut();
|
|
7428
|
+
}
|
|
7368
7429
|
zoomIn() {
|
|
7369
7430
|
this.setScale((this.transform.scale ?? 1) + this.scaleStep);
|
|
7370
7431
|
}
|
|
7371
7432
|
zoomOut() {
|
|
7372
7433
|
this.setScale((this.transform.scale ?? 1) - this.scaleStep);
|
|
7373
7434
|
}
|
|
7435
|
+
rotateLeft() {
|
|
7436
|
+
this.canvasRotation = (this.canvasRotation + 3) % 4;
|
|
7437
|
+
}
|
|
7438
|
+
rotateRight() {
|
|
7439
|
+
this.canvasRotation = (this.canvasRotation + 1) % 4;
|
|
7440
|
+
}
|
|
7374
7441
|
resetZoom() {
|
|
7375
7442
|
this.setScale(1);
|
|
7376
7443
|
}
|
|
7444
|
+
reset() {
|
|
7445
|
+
this.canvasRotation = 0;
|
|
7446
|
+
this.transform = { scale: 1, translateH: 0, translateV: 0, translateUnit: 'px' };
|
|
7447
|
+
}
|
|
7377
7448
|
confirm() {
|
|
7378
7449
|
if (!this.croppedBlob)
|
|
7379
7450
|
return;
|
|
@@ -7388,14 +7459,14 @@ class LogoCropperComponent {
|
|
|
7388
7459
|
this.transform = { ...this.transform, scale: clamped };
|
|
7389
7460
|
}
|
|
7390
7461
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: LogoCropperComponent, deps: [{ token: i4$1.NgbActiveModal }, { token: i3.UtilityService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7391
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.16", type: LogoCropperComponent, isStandalone: true, selector: "BBSF-LogoCropper", inputs: { imageFile: "imageFile", isHorizontal: "isHorizontal", aspectRatio: "aspectRatio", maintainAspectRatio: "maintainAspectRatio", cropperStaticWidth: "cropperStaticWidth", cropperStaticHeight: "cropperStaticHeight", onlyScaleDown: "onlyScaleDown" }, ngImport: i0, template: "<div class=\"bbsf-logo-cropper\">\n <div class=\"modal-header\">\n <h5 class=\"modal-title\">{{ translate(\"CropImage\") }}</h5>\n <button type=\"button\" class=\"btn-close\" (click)=\"dismiss()\"></button>\n </div>\n\n <div class=\"modal-body d-flex flex-column justify-content-center align-items-center py-0\" style=\"min-height: 200px;\">\n <image-cropper [class.crop-horizontal]=\"isHorizontal\" [class.crop-vertical]=\"!isHorizontal\" [imageFile]=\"imageFile\"\n [maintainAspectRatio]=\"maintainAspectRatio\" [aspectRatio]=\"
|
|
7462
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.16", type: LogoCropperComponent, isStandalone: true, selector: "BBSF-LogoCropper", inputs: { imageFile: "imageFile", isHorizontal: "isHorizontal", aspectRatio: "aspectRatio", maintainAspectRatio: "maintainAspectRatio", cropperStaticWidth: "cropperStaticWidth", cropperStaticHeight: "cropperStaticHeight", onlyScaleDown: "onlyScaleDown", allowMoveImage: "allowMoveImage", containWithinAspectRatio: "containWithinAspectRatio", maxDisplayWidth: "maxDisplayWidth", maxDisplayHeight: "maxDisplayHeight" }, ngImport: i0, template: "<div class=\"bbsf-logo-cropper\">\r\n <div class=\"modal-header\">\r\n <h5 class=\"modal-title\">{{ translate(\"CropImage\") }}</h5>\r\n <button type=\"button\" class=\"btn-close\" (click)=\"dismiss()\"></button>\r\n </div>\r\n\r\n <div class=\"modal-body d-flex flex-column justify-content-center align-items-center py-0\" style=\"min-height: 200px;\">\r\n <image-cropper [class.crop-horizontal]=\"isHorizontal\" [class.crop-vertical]=\"!isHorizontal\"\r\n [class.allow-move]=\"allowMoveImage\" [imageFile]=\"imageFile\"\r\n [style.--bbsf-cropper-source-max-height]=\"sourceMaxHeight\"\r\n [style.--bbsf-cropper-source-max-width]=\"sourceMaxWidth\"\r\n [maintainAspectRatio]=\"maintainAspectRatio\" [aspectRatio]=\"effectiveAspectRatio\"\r\n [containWithinAspectRatio]=\"containWithinAspectRatio\" [canvasRotation]=\"canvasRotation\"\r\n [cropperStaticWidth]=\"displayWidth\" [cropperStaticHeight]=\"displayHeight\"\r\n [resizeToWidth]=\"displayScalingEnabled ? cropperStaticWidth : 0\"\r\n [resizeToHeight]=\"displayScalingEnabled ? cropperStaticHeight : 0\"\r\n [onlyScaleDown]=\"onlyScaleDown\" [allowMoveImage]=\"allowMoveImage\" [(transform)]=\"transform\" format=\"png\"\r\n (imageCropped)=\"imageCropped($event)\" (loadImageFailed)=\"imageLoadFailed($event)\" (wheel)=\"onWheel($event)\">\r\n </image-cropper>\r\n\r\n <div class=\"d-flex justify-content-center align-items-center gap-2 mt-3\">\r\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm\" (click)=\"zoomOut()\"\r\n [title]=\"translate('ZoomOut')\">\r\n <i class=\"fa fa-search-minus\"></i>\r\n </button>\r\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm\" (click)=\"zoomIn()\" [title]=\"translate('ZoomIn')\">\r\n <i class=\"fa fa-search-plus\"></i>\r\n </button>\r\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm\" (click)=\"rotateLeft()\"\r\n [title]=\"translate('RotateLeft')\">\r\n <i class=\"fa fa-rotate-left\"></i>\r\n </button>\r\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm\" (click)=\"rotateRight()\"\r\n [title]=\"translate('RotateRight')\">\r\n <i class=\"fa fa-rotate-right\"></i>\r\n </button>\r\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm\" (click)=\"reset()\"\r\n [title]=\"translate('ResetZoom')\">\r\n <i class=\"fa fa-refresh\"></i>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <div class=\"modal-footer\">\r\n <button type=\"button\" class=\"btn btn-sm btn-light\" (click)=\"dismiss()\"> Cancel </button>\r\n <button type=\"button\" class=\"btn btn-sm btn-brand\" [disabled]=\"!croppedBlob\" (click)=\"confirm()\">\r\n {{ translate(\"Crop\") }}\r\n </button>\r\n </div>\r\n</div>\r\n", styles: [".bbsf-logo-cropper image-cropper.crop-horizontal .ngx-ic-source-image{max-height:var(--bbsf-cropper-source-max-height, 45px)!important;max-width:none!important}.bbsf-logo-cropper image-cropper.crop-vertical .ngx-ic-source-image{max-width:var(--bbsf-cropper-source-max-width, 200px)!important;max-height:none!important}.bbsf-logo-cropper image-cropper.allow-move .ngx-ic-cropper{pointer-events:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ImageCropperComponent, selector: "image-cropper", inputs: ["imageChangedEvent", "imageURL", "imageBase64", "imageFile", "imageAltText", "options", "cropperFrameAriaLabel", "output", "format", "autoCrop", "cropper", "transform", "maintainAspectRatio", "aspectRatio", "resetCropOnAspectRatioChange", "resizeToWidth", "resizeToHeight", "cropperMinWidth", "cropperMinHeight", "cropperMaxHeight", "cropperMaxWidth", "cropperStaticWidth", "cropperStaticHeight", "canvasRotation", "initialStepSize", "roundCropper", "onlyScaleDown", "imageQuality", "backgroundColor", "containWithinAspectRatio", "hideResizeSquares", "allowMoveImage", "checkImageType", "alignImage", "disabled", "hidden"], outputs: ["imageCropped", "startCropImage", "imageLoaded", "cropperReady", "loadImageFailed", "transformChange", "cropperChange"] }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
7392
7463
|
}
|
|
7393
7464
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: LogoCropperComponent, decorators: [{
|
|
7394
7465
|
type: Component,
|
|
7395
7466
|
args: [{ selector: 'BBSF-LogoCropper', encapsulation: ViewEncapsulation.None, standalone: true, imports: [
|
|
7396
7467
|
CommonModule,
|
|
7397
7468
|
ImageCropperComponent
|
|
7398
|
-
], template: "<div class=\"bbsf-logo-cropper\">\n <div class=\"modal-header\">\n <h5 class=\"modal-title\">{{ translate(\"CropImage\") }}</h5>\n <button type=\"button\" class=\"btn-close\" (click)=\"dismiss()\"></button>\n </div>\n\n <div class=\"modal-body d-flex flex-column justify-content-center align-items-center py-0\" style=\"min-height: 200px;\">\n <image-cropper [class.crop-horizontal]=\"isHorizontal\" [class.crop-vertical]=\"!isHorizontal\" [imageFile]=\"imageFile\"\n [maintainAspectRatio]=\"maintainAspectRatio\" [aspectRatio]=\"
|
|
7469
|
+
], template: "<div class=\"bbsf-logo-cropper\">\r\n <div class=\"modal-header\">\r\n <h5 class=\"modal-title\">{{ translate(\"CropImage\") }}</h5>\r\n <button type=\"button\" class=\"btn-close\" (click)=\"dismiss()\"></button>\r\n </div>\r\n\r\n <div class=\"modal-body d-flex flex-column justify-content-center align-items-center py-0\" style=\"min-height: 200px;\">\r\n <image-cropper [class.crop-horizontal]=\"isHorizontal\" [class.crop-vertical]=\"!isHorizontal\"\r\n [class.allow-move]=\"allowMoveImage\" [imageFile]=\"imageFile\"\r\n [style.--bbsf-cropper-source-max-height]=\"sourceMaxHeight\"\r\n [style.--bbsf-cropper-source-max-width]=\"sourceMaxWidth\"\r\n [maintainAspectRatio]=\"maintainAspectRatio\" [aspectRatio]=\"effectiveAspectRatio\"\r\n [containWithinAspectRatio]=\"containWithinAspectRatio\" [canvasRotation]=\"canvasRotation\"\r\n [cropperStaticWidth]=\"displayWidth\" [cropperStaticHeight]=\"displayHeight\"\r\n [resizeToWidth]=\"displayScalingEnabled ? cropperStaticWidth : 0\"\r\n [resizeToHeight]=\"displayScalingEnabled ? cropperStaticHeight : 0\"\r\n [onlyScaleDown]=\"onlyScaleDown\" [allowMoveImage]=\"allowMoveImage\" [(transform)]=\"transform\" format=\"png\"\r\n (imageCropped)=\"imageCropped($event)\" (loadImageFailed)=\"imageLoadFailed($event)\" (wheel)=\"onWheel($event)\">\r\n </image-cropper>\r\n\r\n <div class=\"d-flex justify-content-center align-items-center gap-2 mt-3\">\r\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm\" (click)=\"zoomOut()\"\r\n [title]=\"translate('ZoomOut')\">\r\n <i class=\"fa fa-search-minus\"></i>\r\n </button>\r\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm\" (click)=\"zoomIn()\" [title]=\"translate('ZoomIn')\">\r\n <i class=\"fa fa-search-plus\"></i>\r\n </button>\r\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm\" (click)=\"rotateLeft()\"\r\n [title]=\"translate('RotateLeft')\">\r\n <i class=\"fa fa-rotate-left\"></i>\r\n </button>\r\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm\" (click)=\"rotateRight()\"\r\n [title]=\"translate('RotateRight')\">\r\n <i class=\"fa fa-rotate-right\"></i>\r\n </button>\r\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm\" (click)=\"reset()\"\r\n [title]=\"translate('ResetZoom')\">\r\n <i class=\"fa fa-refresh\"></i>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <div class=\"modal-footer\">\r\n <button type=\"button\" class=\"btn btn-sm btn-light\" (click)=\"dismiss()\"> Cancel </button>\r\n <button type=\"button\" class=\"btn btn-sm btn-brand\" [disabled]=\"!croppedBlob\" (click)=\"confirm()\">\r\n {{ translate(\"Crop\") }}\r\n </button>\r\n </div>\r\n</div>\r\n", styles: [".bbsf-logo-cropper image-cropper.crop-horizontal .ngx-ic-source-image{max-height:var(--bbsf-cropper-source-max-height, 45px)!important;max-width:none!important}.bbsf-logo-cropper image-cropper.crop-vertical .ngx-ic-source-image{max-width:var(--bbsf-cropper-source-max-width, 200px)!important;max-height:none!important}.bbsf-logo-cropper image-cropper.allow-move .ngx-ic-cropper{pointer-events:none}\n"] }]
|
|
7399
7470
|
}], ctorParameters: () => [{ type: i4$1.NgbActiveModal }, { type: i3.UtilityService }], propDecorators: { imageFile: [{
|
|
7400
7471
|
type: Input
|
|
7401
7472
|
}], isHorizontal: [{
|
|
@@ -7410,6 +7481,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.16", ngImpo
|
|
|
7410
7481
|
type: Input
|
|
7411
7482
|
}], onlyScaleDown: [{
|
|
7412
7483
|
type: Input
|
|
7484
|
+
}], allowMoveImage: [{
|
|
7485
|
+
type: Input
|
|
7486
|
+
}], containWithinAspectRatio: [{
|
|
7487
|
+
type: Input
|
|
7488
|
+
}], maxDisplayWidth: [{
|
|
7489
|
+
type: Input
|
|
7490
|
+
}], maxDisplayHeight: [{
|
|
7491
|
+
type: Input
|
|
7413
7492
|
}] } });
|
|
7414
7493
|
|
|
7415
7494
|
class FileUploadComponent {
|
|
@@ -7755,7 +7834,7 @@ class FileUploadComponent {
|
|
|
7755
7834
|
const formControl = this.fileUploadFormControl;
|
|
7756
7835
|
const resourceValue = this.UtilityService.getResourceValue('DuplicateFileError');
|
|
7757
7836
|
// If resource value is the same as the key, it means the resource wasn't found
|
|
7758
|
-
const errorMessage = (resourceValue && resourceValue !== 'DuplicateFileError') ? resourceValue : 'File(s) already exist';
|
|
7837
|
+
const errorMessage = this.options?.duplicateFileErrorMsg || ((resourceValue && resourceValue !== 'DuplicateFileError') ? resourceValue : 'File(s) already exist');
|
|
7759
7838
|
const errorObj = {
|
|
7760
7839
|
DuplicateFileError: `${errorMessage}: ${duplicateFileNames}`
|
|
7761
7840
|
};
|
|
@@ -7794,7 +7873,7 @@ class FileUploadComponent {
|
|
|
7794
7873
|
//keep existing valid files, remove only extra ones
|
|
7795
7874
|
this.uploader.queue = this.uploader.queue.slice(0, max);
|
|
7796
7875
|
this.maxFilesReached = true;
|
|
7797
|
-
this.maxFilesValidationMsg = this.UtilityService.getResourceValue('MaxFileCountValidationKey') + max;
|
|
7876
|
+
this.maxFilesValidationMsg = (this.options.maxFileCountErrorMsg || this.UtilityService.getResourceValue('MaxFileCountValidationKey')) + max;
|
|
7798
7877
|
}
|
|
7799
7878
|
else {
|
|
7800
7879
|
this.maxFilesReached = false;
|
|
@@ -8002,6 +8081,10 @@ class FileUploadComponent {
|
|
|
8002
8081
|
modalRef.componentInstance.cropperStaticHeight = preset.cropperStaticHeight;
|
|
8003
8082
|
modalRef.componentInstance.maintainAspectRatio = cropperOptions.maintainAspectRatio;
|
|
8004
8083
|
modalRef.componentInstance.onlyScaleDown = cropperOptions.onlyScaleDown;
|
|
8084
|
+
modalRef.componentInstance.allowMoveImage = cropperOptions.allowMoveImage;
|
|
8085
|
+
modalRef.componentInstance.containWithinAspectRatio = cropperOptions.containWithinAspectRatio;
|
|
8086
|
+
modalRef.componentInstance.maxDisplayWidth = cropperOptions.maxDisplayWidth;
|
|
8087
|
+
modalRef.componentInstance.maxDisplayHeight = cropperOptions.maxDisplayHeight;
|
|
8005
8088
|
modalRef.result.then((croppedFile) => {
|
|
8006
8089
|
this.readCroppedFile(addedFile, croppedFile);
|
|
8007
8090
|
}).catch(() => {
|