@progress/kendo-angular-spreadsheet 18.5.0-develop.1 → 18.5.0-develop.11

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.
@@ -0,0 +1,468 @@
1
+ /**-----------------------------------------------------------------------------------------
2
+ * Copyright © 2025 Progress Software Corporation. All rights reserved.
3
+ * Licensed under commercial license. See LICENSE.md in the project root for more information
4
+ *-------------------------------------------------------------------------------------------*/
5
+ import { NgIf } from '@angular/common';
6
+ import { Component, EventEmitter, NgZone, Output } from '@angular/core';
7
+ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
8
+ import { DropDownListComponent } from '@progress/kendo-angular-dropdowns';
9
+ import { CheckBoxComponent, RadioButtonComponent, TextBoxComponent } from '@progress/kendo-angular-inputs';
10
+ import { LocalizationService } from '@progress/kendo-angular-l10n';
11
+ import { rangeValidator } from './utils';
12
+ import { SpreadsheetService } from '../common/spreadsheet.service';
13
+ import { isPresent } from '@progress/kendo-angular-common';
14
+ import { LabelComponent } from '@progress/kendo-angular-label';
15
+ import { DialogActionsComponent, DialogContentBase, DialogRef } from '@progress/kendo-angular-dialog';
16
+ import { ButtonComponent } from '@progress/kendo-angular-buttons';
17
+ import * as i0 from "@angular/core";
18
+ import * as i1 from "@progress/kendo-angular-l10n";
19
+ import * as i2 from "../common/spreadsheet.service";
20
+ import * as i3 from "@progress/kendo-angular-dialog";
21
+ import * as i4 from "@angular/forms";
22
+ const COMPARER_VALUE_NUMBER = {
23
+ greaterThan: 'validationMinValue',
24
+ lessThan: 'validationMaxValue',
25
+ greaterThanOrEqualTo: 'validationMinValue',
26
+ lessThanOrEqualTo: 'validationMaxValue'
27
+ };
28
+ const COMPARER_VALUE_DATE = {
29
+ greaterThan: 'validationStartValue',
30
+ lessThan: 'validationEndValue'
31
+ };
32
+ /**
33
+ * @hidden
34
+ */
35
+ export class DataValidationDialogComponent extends DialogContentBase {
36
+ localization;
37
+ spreadsheetService;
38
+ zone;
39
+ cellRange;
40
+ criteriaList;
41
+ ignoreBlank = true;
42
+ showButton = true;
43
+ showHint;
44
+ from = null;
45
+ to = null;
46
+ validationFormGroup;
47
+ hintTitle = 'Validation Error';
48
+ hintMessage = 'The value that you entered violates the validation rules set on the cell.';
49
+ _comparer;
50
+ _criteria;
51
+ _onInvalidData;
52
+ onInvalidDataSub;
53
+ allComparers = [];
54
+ textComparers = [];
55
+ dialogAction = new EventEmitter();
56
+ constructor(localization, spreadsheetService, zone, dialog) {
57
+ super(dialog);
58
+ this.localization = localization;
59
+ this.spreadsheetService = spreadsheetService;
60
+ this.zone = zone;
61
+ }
62
+ ngOnInit() {
63
+ if (this.criteria.type === 'any') {
64
+ this.validationFormGroup = new FormGroup({
65
+ range: new FormControl(this.cellRange, [Validators.required, rangeValidator(this)])
66
+ });
67
+ }
68
+ else if (this.comparer?.type === 'between' || this.comparer?.type === 'notBetween') {
69
+ this.validationFormGroup = new FormGroup({
70
+ range: new FormControl(this.cellRange, [Validators.required, rangeValidator(this)]),
71
+ min: new FormControl(this.from, [Validators.required]),
72
+ max: new FormControl(this.to, [Validators.required]),
73
+ onInvalidData: new FormControl(this.onInvalidData)
74
+ });
75
+ }
76
+ else {
77
+ this.validationFormGroup = new FormGroup({
78
+ range: new FormControl(this.cellRange, [Validators.required, rangeValidator(this)]),
79
+ min: new FormControl(this.from, [Validators.required]),
80
+ onInvalidData: new FormControl(this.onInvalidData)
81
+ });
82
+ }
83
+ this.addInvalidDataSub();
84
+ this.criteriaList = [
85
+ { type: 'any', name: this.getLocalizationMessage('anyValueValidationCriteria') },
86
+ { type: 'number', name: this.getLocalizationMessage('numberValidationCriteria') },
87
+ { type: 'text', name: this.getLocalizationMessage('textValidationCriteria') },
88
+ { type: 'date', name: this.getLocalizationMessage('dateValidationCriteria') },
89
+ { type: 'custom', name: this.getLocalizationMessage('customFormulaValidationCriteria') },
90
+ { type: 'list', name: this.getLocalizationMessage('listValidationCriteria') }
91
+ ];
92
+ this.allComparers = [
93
+ { type: 'greaterThan', name: this.getLocalizationMessage('greaterThanValidationComparer') },
94
+ { type: 'lessThan', name: this.getLocalizationMessage('lessThanValidationComparer') },
95
+ { type: 'between', name: this.getLocalizationMessage('betweenValidationComparer') },
96
+ { type: 'notBetween', name: this.getLocalizationMessage('notBetweenValidationComparer') },
97
+ { type: 'equalTo', name: this.getLocalizationMessage('equalToValidationComparer') },
98
+ { type: 'notEqualTo', name: this.getLocalizationMessage('notEqualToValidationComparer') },
99
+ { type: 'greaterThanOrEqualTo', name: this.getLocalizationMessage('greaterThanOrEqualToValidationComparer') },
100
+ { type: 'lessThanOrEqualTo', name: this.getLocalizationMessage('lessThanOrEqualToValidationComparer') }
101
+ ];
102
+ this.textComparers = [
103
+ { type: 'equalTo', name: this.getLocalizationMessage('equalToValidationComparer') },
104
+ { type: 'notEqualTo', name: this.getLocalizationMessage('notEqualToValidationComparer') }
105
+ ];
106
+ }
107
+ ngOnDestroy() {
108
+ this.onInvalidDataSub?.unsubscribe();
109
+ this.onInvalidDataSub = null;
110
+ }
111
+ getLocalizationMessage(key) {
112
+ return this.localization.get(key);
113
+ }
114
+ onRangeChange(value, input) {
115
+ let range;
116
+ try {
117
+ range = this.spreadsheetService.spreadsheet.activeSheet().range(value);
118
+ }
119
+ catch (e) { /** noop */ }
120
+ if (isPresent(range)) {
121
+ range.select();
122
+ this.cellRange = value;
123
+ }
124
+ this.zone.runOutsideAngular(() => {
125
+ setTimeout(() => {
126
+ input.focus();
127
+ });
128
+ });
129
+ }
130
+ onSelectionChange(item, field) {
131
+ if (field === 'criteria') {
132
+ this.from = this.to = null;
133
+ this.comparer = null;
134
+ this.showButton = this.ignoreBlank = true;
135
+ if (item.type === 'any') {
136
+ this.validationFormGroup = new FormGroup({
137
+ range: new FormControl(this.cellRange, [Validators.required, rangeValidator(this)])
138
+ });
139
+ }
140
+ else {
141
+ this.validationFormGroup = new FormGroup({
142
+ range: new FormControl(this.cellRange, [Validators.required, rangeValidator(this)]),
143
+ min: new FormControl(this.from, [Validators.required]),
144
+ onInvalidData: new FormControl(this.onInvalidData)
145
+ });
146
+ }
147
+ }
148
+ else {
149
+ this.showButton = this.ignoreBlank = true;
150
+ switch (item.type) {
151
+ case 'between':
152
+ case 'notBetween':
153
+ this.validationFormGroup = new FormGroup({
154
+ range: new FormControl(this.cellRange, [Validators.required, rangeValidator(this)]),
155
+ min: new FormControl(this.from, [Validators.required]),
156
+ max: new FormControl(this.to, [Validators.required]),
157
+ onInvalidData: new FormControl(this.onInvalidData)
158
+ });
159
+ break;
160
+ default:
161
+ this.validationFormGroup = new FormGroup({
162
+ range: new FormControl(this.cellRange, [Validators.required, rangeValidator(this)]),
163
+ min: new FormControl(this.from, [Validators.required]),
164
+ onInvalidData: new FormControl(this.onInvalidData)
165
+ });
166
+ break;
167
+ }
168
+ }
169
+ this.addInvalidDataSub();
170
+ this[field] = item;
171
+ }
172
+ get criteria() {
173
+ return this._criteria || { type: 'any', name: 'Any value' };
174
+ }
175
+ set criteria(value) {
176
+ this._criteria = value;
177
+ }
178
+ get comparer() {
179
+ return this._comparer || this.comparerList[0];
180
+ }
181
+ set comparer(value) {
182
+ this._comparer = value;
183
+ }
184
+ get onInvalidData() {
185
+ return this._onInvalidData || 'reject';
186
+ }
187
+ set onInvalidData(value) {
188
+ this._onInvalidData = value;
189
+ }
190
+ get comparerList() {
191
+ return this.criteria.type === 'text' ? this.textComparers : this.allComparers;
192
+ }
193
+ get singleValue() {
194
+ return !this.comparer.name.includes('between');
195
+ }
196
+ get showComparer() {
197
+ return this.criteria.type === 'number' || this.criteria.type === 'text' || this.criteria.type === 'date';
198
+ }
199
+ setData(args) {
200
+ for (const key in args) {
201
+ this[key] = args[key];
202
+ }
203
+ }
204
+ getValueLabel() {
205
+ const valueLabel = this.localization.get('validationValue');
206
+ if (this.criteria.type === 'custom' || this.criteria.type === 'list') {
207
+ return valueLabel;
208
+ }
209
+ const labelKey = this.criteria.type === 'date' ? COMPARER_VALUE_DATE[this.comparer.type] : COMPARER_VALUE_NUMBER[this.comparer.type];
210
+ return this.localization.get(labelKey) || valueLabel;
211
+ }
212
+ getShowButtonLabel() {
213
+ const labelKey = this.criteria.type === 'list' ? 'validationShowListButtonCheckbox' : 'validationShowDateButtonCheckbox';
214
+ return this.getLocalizationMessage(labelKey);
215
+ }
216
+ get validationPresent() {
217
+ const currentSheet = this.spreadsheetService.spreadsheet.activeSheet();
218
+ return !!currentSheet.range(currentSheet.activeCell()).validation();
219
+ }
220
+ addInvalidDataSub() {
221
+ this.onInvalidDataSub?.unsubscribe();
222
+ if (this.validationFormGroup.get('onInvalidData')) {
223
+ this.onInvalidDataSub = this.validationFormGroup.get('onInvalidData').valueChanges.subscribe((value) => {
224
+ this.onInvalidData = value;
225
+ });
226
+ }
227
+ }
228
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DataValidationDialogComponent, deps: [{ token: i1.LocalizationService }, { token: i2.SpreadsheetService }, { token: i0.NgZone }, { token: i3.DialogRef }], target: i0.ɵɵFactoryTarget.Component });
229
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: DataValidationDialogComponent, isStandalone: true, selector: "ng-component", outputs: { dialogAction: "dialogAction" }, usesInheritance: true, ngImport: i0, template: `
230
+ <form class="k-form k-form-md" [formGroup]="validationFormGroup">
231
+ <div class="k-form-field">
232
+ <kendo-label class="k-form-label" [text]="getLocalizationMessage('validationCellRange')" [for]="rangeInput"></kendo-label>
233
+ <div class="k-form-field-wrap">
234
+ <kendo-textbox #rangeInput
235
+ [clearButton]="true"
236
+ [value]="cellRange"
237
+ (valueChange)="onRangeChange($event, rangeInput)"
238
+ formControlName="range">
239
+ </kendo-textbox>
240
+ </div>
241
+ </div>
242
+ <div class="k-form-field">
243
+ <kendo-label class="k-form-label" [text]="getLocalizationMessage('validationCriteria')" [for]="criteriaInput"></kendo-label>
244
+ <div class="k-form-field-wrap">
245
+ <kendo-dropdownlist #criteriaInput
246
+ [data]="criteriaList"
247
+ [value]="criteria"
248
+ (selectionChange)="onSelectionChange($event, 'criteria')"
249
+ textField="name"
250
+ valueField="type">
251
+ </kendo-dropdownlist>
252
+ </div>
253
+ </div>
254
+ <ng-container *ngIf="criteria.type !== 'any'">
255
+ <div class="k-form-field" *ngIf="showComparer">
256
+ <kendo-label class="k-form-label" [text]="getLocalizationMessage('validationComparer')" [for]="comparerInput"></kendo-label>
257
+ <div class="k-form-field-wrap">
258
+ <kendo-dropdownlist #comparerInput
259
+ [data]="comparerList"
260
+ [value]="comparer"
261
+ (selectionChange)="onSelectionChange($event, 'comparer')"
262
+ textField="name"
263
+ valueField="type">
264
+ </kendo-dropdownlist>
265
+ </div>
266
+ </div>
267
+ <div class="k-form-field" *ngIf="singleValue; else betweenValues">
268
+ <kendo-label class="k-label k-form-label" [text]="getValueLabel()" [for]="minInput"></kendo-label>
269
+ <div class="k-form-field-wrap">
270
+ <kendo-textbox #minInput [(value)]="from" formControlName="min"></kendo-textbox>
271
+ </div>
272
+ </div>
273
+ <ng-template #betweenValues>
274
+ <div class="k-form-field">
275
+ <kendo-label class="k-form-label" [text]="getLocalizationMessage(criteria.type === 'date' ? 'validationStartValue' : 'validationMinValue')" [for]="betweenMinInput"></kendo-label>
276
+ <div class="k-form-field-wrap">
277
+ <kendo-textbox #betweenMinInput [(value)]="from" formControlName="min"></kendo-textbox>
278
+ </div>
279
+ </div>
280
+ <div class="k-form-field">
281
+ <kendo-label class="k-form-label" [text]="getLocalizationMessage(criteria.type === 'date' ? 'validationEndValue' : 'validationMaxValue')" [for]="betweenMaxInput"></kendo-label>
282
+ <div class="k-form-field-wrap">
283
+ <kendo-textbox #betweenMaxInput [(value)]="to" formControlName="max"></kendo-textbox>
284
+ </div>
285
+ </div>
286
+ </ng-template>
287
+ <div class="k-form-field" *ngIf="criteria.type === 'list'">
288
+ <div class="k-form-field-wrap">
289
+ <kendo-checkbox #showBtnCheckBox [(checkedState)]="showButton"></kendo-checkbox>
290
+ <kendo-label class="k-checkbox-label" [for]="showBtnCheckBox" [text]="getLocalizationMessage('validationShowListButtonCheckbox')"></kendo-label>
291
+ </div>
292
+ </div>
293
+ <div class="k-form-field">
294
+ <div class="k-form-field-wrap">
295
+ <kendo-checkbox #ignoreBlankCheckBox [(checkedState)]="ignoreBlank"></kendo-checkbox>
296
+ <kendo-label class="k-checkbox-label" [for]="ignoreBlankCheckBox" [text]="getLocalizationMessage('validationIgnoreBlankCheckbox')"></kendo-label>
297
+ </div>
298
+ </div>
299
+ <div class="k-form-field">
300
+ <kendo-label class="k-form-label" [text]="getLocalizationMessage('validationOnInvalidData')"></kendo-label>
301
+ <div class="k-form-field-wrap">
302
+ <ul class="k-radio-list k-list-vertical">
303
+ <li class="k-radio-list-item">
304
+ <kendo-radiobutton #rejectRadioBtn formControlName="onInvalidData" value="reject"></kendo-radiobutton>
305
+ <kendo-label class="k-radio-label" [for]="rejectRadioBtn" [text]="getLocalizationMessage('validationRejectInput')"></kendo-label>
306
+ </li>
307
+ <li class="k-radio-list-item">
308
+ <kendo-radiobutton #warningRadioBtn formControlName="onInvalidData" value="warning"></kendo-radiobutton>
309
+ <kendo-label class="k-radio-label" [for]="warningRadioBtn" [text]="getLocalizationMessage('validationShowWarning')"></kendo-label>
310
+ </li>
311
+ </ul>
312
+ </div>
313
+ </div>
314
+ <div class="k-form-field">
315
+ <div class="k-form-field-wrap">
316
+ <kendo-checkbox #showHintCheckBox [(checkedState)]="showHint"></kendo-checkbox>
317
+ <kendo-label class="k-checkbox-label" [for]="showHintCheckBox" [text]="getLocalizationMessage('validationShowHint')"></kendo-label>
318
+ </div>
319
+ </div>
320
+ <div class="k-form-field">
321
+ <kendo-label class="k-form-label" [text]="getLocalizationMessage('validationHintTitle')" [for]="hintTitleInput"></kendo-label>
322
+ <div class="k-form-field-wrap">
323
+ <kendo-textbox #hintTitleInput [(value)]="hintTitle"></kendo-textbox>
324
+ </div>
325
+ </div>
326
+ <div class="k-form-field">
327
+ <kendo-label class="k-form-label" [text]="getLocalizationMessage('validationHintMessage')" [for]="hintMessageInput"></kendo-label>
328
+ <div class="k-form-field-wrap">
329
+ <kendo-textbox #hintMessageInput [(value)]="hintMessage"></kendo-textbox>
330
+ </div>
331
+ </div>
332
+ </ng-container>
333
+ </form>
334
+ <kendo-dialog-actions layout="start" #dialogActions>
335
+ <button kendoButton themeColor="primary" [disabled]="validationFormGroup?.invalid" (click)="dialogAction.emit(getLocalizationMessage('dialogApply'))">
336
+ {{getLocalizationMessage('dialogApply')}}
337
+ </button>
338
+ <button kendoButton (click)="dialogAction.emit(getLocalizationMessage('dialogCancel'))">{{getLocalizationMessage('dialogCancel')}}</button>
339
+ <button kendoButton themeColor="error" fillMode="flat" *ngIf="validationPresent" (click)="dialogAction.emit(getLocalizationMessage('dialogRemove'))">
340
+ {{getLocalizationMessage('dialogRemove')}}
341
+ </button>
342
+ </kendo-dialog-actions>
343
+ `, isInline: true, dependencies: [{ kind: "component", type: TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "component", type: DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "title", "subtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "component", type: CheckBoxComponent, selector: "kendo-checkbox", inputs: ["checkedState", "rounded"], outputs: ["checkedStateChange"], exportAs: ["kendoCheckBox"] }, { kind: "component", type: RadioButtonComponent, selector: "kendo-radiobutton", inputs: ["checked"], outputs: ["checkedChange"], exportAs: ["kendoRadioButton"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i4.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: LabelComponent, selector: "kendo-label", inputs: ["text", "for", "optional", "labelCssStyle", "labelCssClass"], exportAs: ["kendoLabel"] }, { kind: "component", type: DialogActionsComponent, selector: "kendo-dialog-actions", inputs: ["actions", "layout"], outputs: ["action"] }, { kind: "component", type: ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }] });
344
+ }
345
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DataValidationDialogComponent, decorators: [{
346
+ type: Component,
347
+ args: [{
348
+ template: `
349
+ <form class="k-form k-form-md" [formGroup]="validationFormGroup">
350
+ <div class="k-form-field">
351
+ <kendo-label class="k-form-label" [text]="getLocalizationMessage('validationCellRange')" [for]="rangeInput"></kendo-label>
352
+ <div class="k-form-field-wrap">
353
+ <kendo-textbox #rangeInput
354
+ [clearButton]="true"
355
+ [value]="cellRange"
356
+ (valueChange)="onRangeChange($event, rangeInput)"
357
+ formControlName="range">
358
+ </kendo-textbox>
359
+ </div>
360
+ </div>
361
+ <div class="k-form-field">
362
+ <kendo-label class="k-form-label" [text]="getLocalizationMessage('validationCriteria')" [for]="criteriaInput"></kendo-label>
363
+ <div class="k-form-field-wrap">
364
+ <kendo-dropdownlist #criteriaInput
365
+ [data]="criteriaList"
366
+ [value]="criteria"
367
+ (selectionChange)="onSelectionChange($event, 'criteria')"
368
+ textField="name"
369
+ valueField="type">
370
+ </kendo-dropdownlist>
371
+ </div>
372
+ </div>
373
+ <ng-container *ngIf="criteria.type !== 'any'">
374
+ <div class="k-form-field" *ngIf="showComparer">
375
+ <kendo-label class="k-form-label" [text]="getLocalizationMessage('validationComparer')" [for]="comparerInput"></kendo-label>
376
+ <div class="k-form-field-wrap">
377
+ <kendo-dropdownlist #comparerInput
378
+ [data]="comparerList"
379
+ [value]="comparer"
380
+ (selectionChange)="onSelectionChange($event, 'comparer')"
381
+ textField="name"
382
+ valueField="type">
383
+ </kendo-dropdownlist>
384
+ </div>
385
+ </div>
386
+ <div class="k-form-field" *ngIf="singleValue; else betweenValues">
387
+ <kendo-label class="k-label k-form-label" [text]="getValueLabel()" [for]="minInput"></kendo-label>
388
+ <div class="k-form-field-wrap">
389
+ <kendo-textbox #minInput [(value)]="from" formControlName="min"></kendo-textbox>
390
+ </div>
391
+ </div>
392
+ <ng-template #betweenValues>
393
+ <div class="k-form-field">
394
+ <kendo-label class="k-form-label" [text]="getLocalizationMessage(criteria.type === 'date' ? 'validationStartValue' : 'validationMinValue')" [for]="betweenMinInput"></kendo-label>
395
+ <div class="k-form-field-wrap">
396
+ <kendo-textbox #betweenMinInput [(value)]="from" formControlName="min"></kendo-textbox>
397
+ </div>
398
+ </div>
399
+ <div class="k-form-field">
400
+ <kendo-label class="k-form-label" [text]="getLocalizationMessage(criteria.type === 'date' ? 'validationEndValue' : 'validationMaxValue')" [for]="betweenMaxInput"></kendo-label>
401
+ <div class="k-form-field-wrap">
402
+ <kendo-textbox #betweenMaxInput [(value)]="to" formControlName="max"></kendo-textbox>
403
+ </div>
404
+ </div>
405
+ </ng-template>
406
+ <div class="k-form-field" *ngIf="criteria.type === 'list'">
407
+ <div class="k-form-field-wrap">
408
+ <kendo-checkbox #showBtnCheckBox [(checkedState)]="showButton"></kendo-checkbox>
409
+ <kendo-label class="k-checkbox-label" [for]="showBtnCheckBox" [text]="getLocalizationMessage('validationShowListButtonCheckbox')"></kendo-label>
410
+ </div>
411
+ </div>
412
+ <div class="k-form-field">
413
+ <div class="k-form-field-wrap">
414
+ <kendo-checkbox #ignoreBlankCheckBox [(checkedState)]="ignoreBlank"></kendo-checkbox>
415
+ <kendo-label class="k-checkbox-label" [for]="ignoreBlankCheckBox" [text]="getLocalizationMessage('validationIgnoreBlankCheckbox')"></kendo-label>
416
+ </div>
417
+ </div>
418
+ <div class="k-form-field">
419
+ <kendo-label class="k-form-label" [text]="getLocalizationMessage('validationOnInvalidData')"></kendo-label>
420
+ <div class="k-form-field-wrap">
421
+ <ul class="k-radio-list k-list-vertical">
422
+ <li class="k-radio-list-item">
423
+ <kendo-radiobutton #rejectRadioBtn formControlName="onInvalidData" value="reject"></kendo-radiobutton>
424
+ <kendo-label class="k-radio-label" [for]="rejectRadioBtn" [text]="getLocalizationMessage('validationRejectInput')"></kendo-label>
425
+ </li>
426
+ <li class="k-radio-list-item">
427
+ <kendo-radiobutton #warningRadioBtn formControlName="onInvalidData" value="warning"></kendo-radiobutton>
428
+ <kendo-label class="k-radio-label" [for]="warningRadioBtn" [text]="getLocalizationMessage('validationShowWarning')"></kendo-label>
429
+ </li>
430
+ </ul>
431
+ </div>
432
+ </div>
433
+ <div class="k-form-field">
434
+ <div class="k-form-field-wrap">
435
+ <kendo-checkbox #showHintCheckBox [(checkedState)]="showHint"></kendo-checkbox>
436
+ <kendo-label class="k-checkbox-label" [for]="showHintCheckBox" [text]="getLocalizationMessage('validationShowHint')"></kendo-label>
437
+ </div>
438
+ </div>
439
+ <div class="k-form-field">
440
+ <kendo-label class="k-form-label" [text]="getLocalizationMessage('validationHintTitle')" [for]="hintTitleInput"></kendo-label>
441
+ <div class="k-form-field-wrap">
442
+ <kendo-textbox #hintTitleInput [(value)]="hintTitle"></kendo-textbox>
443
+ </div>
444
+ </div>
445
+ <div class="k-form-field">
446
+ <kendo-label class="k-form-label" [text]="getLocalizationMessage('validationHintMessage')" [for]="hintMessageInput"></kendo-label>
447
+ <div class="k-form-field-wrap">
448
+ <kendo-textbox #hintMessageInput [(value)]="hintMessage"></kendo-textbox>
449
+ </div>
450
+ </div>
451
+ </ng-container>
452
+ </form>
453
+ <kendo-dialog-actions layout="start" #dialogActions>
454
+ <button kendoButton themeColor="primary" [disabled]="validationFormGroup?.invalid" (click)="dialogAction.emit(getLocalizationMessage('dialogApply'))">
455
+ {{getLocalizationMessage('dialogApply')}}
456
+ </button>
457
+ <button kendoButton (click)="dialogAction.emit(getLocalizationMessage('dialogCancel'))">{{getLocalizationMessage('dialogCancel')}}</button>
458
+ <button kendoButton themeColor="error" fillMode="flat" *ngIf="validationPresent" (click)="dialogAction.emit(getLocalizationMessage('dialogRemove'))">
459
+ {{getLocalizationMessage('dialogRemove')}}
460
+ </button>
461
+ </kendo-dialog-actions>
462
+ `,
463
+ standalone: true,
464
+ imports: [TextBoxComponent, DropDownListComponent, CheckBoxComponent, RadioButtonComponent, NgIf, ReactiveFormsModule, LabelComponent, DialogActionsComponent, ButtonComponent]
465
+ }]
466
+ }], ctorParameters: function () { return [{ type: i1.LocalizationService }, { type: i2.SpreadsheetService }, { type: i0.NgZone }, { type: i3.DialogRef }]; }, propDecorators: { dialogAction: [{
467
+ type: Output
468
+ }] } });
@@ -0,0 +1,156 @@
1
+ /**-----------------------------------------------------------------------------------------
2
+ * Copyright © 2025 Progress Software Corporation. All rights reserved.
3
+ * Licensed under commercial license. See LICENSE.md in the project root for more information
4
+ *-------------------------------------------------------------------------------------------*/
5
+ import { Directive, Renderer2 } from '@angular/core';
6
+ import { take } from 'rxjs/operators';
7
+ import { ToolBarButtonComponent } from '@progress/kendo-angular-toolbar';
8
+ import { DialogService } from '@progress/kendo-angular-dialog';
9
+ import { SpreadsheetLocalizationService } from '../localization/spreadsheet-localization.service';
10
+ import { SpreadsheetService } from '../common/spreadsheet.service';
11
+ import { SpreadsheetToolsService } from './tools.service';
12
+ import { SpreadsheetCommandButton } from './shared/spreadsheet-command-button';
13
+ import { DataValidationDialogComponent } from './data-validation-dialog.component';
14
+ import * as i0 from "@angular/core";
15
+ import * as i1 from "@progress/kendo-angular-toolbar";
16
+ import * as i2 from "../localization/spreadsheet-localization.service";
17
+ import * as i3 from "../common/spreadsheet.service";
18
+ import * as i4 from "./tools.service";
19
+ import * as i5 from "@progress/kendo-angular-dialog";
20
+ const COMPARERS_LIST = [
21
+ { type: 'greaterThan', name: 'greater than' },
22
+ { type: 'lessThan', name: 'less than' },
23
+ { type: 'between', name: 'between' },
24
+ { type: 'notBetween', name: 'not between' },
25
+ { type: 'equalTo', name: 'equal to' },
26
+ { type: 'notEqualTo', name: 'not equal to' },
27
+ { type: 'greaterThanOrEqualTo', name: 'greater than or equal to' },
28
+ { type: 'lessThanOrEqualTo', name: 'less than or equal to' }
29
+ ];
30
+ const CRITERIA_LIST = [
31
+ { type: 'any', name: 'Any value' },
32
+ { type: 'number', name: 'Number' },
33
+ { type: 'text', name: 'Text' },
34
+ { type: 'date', name: 'Date' },
35
+ { type: 'custom', name: 'Custom Formula' },
36
+ { type: 'list', name: 'List' }
37
+ ];
38
+ /**
39
+ * @hidden
40
+ */
41
+ export class SpreadsheetDataValidationDirective extends SpreadsheetCommandButton {
42
+ dialogService;
43
+ renderer;
44
+ constructor(button, localization, spreadsheetService, toolsService, dialogService, renderer) {
45
+ super('dataValidation', button, localization, spreadsheetService, toolsService, {
46
+ command: 'EditValidationCommand', options: { value: '' }
47
+ });
48
+ this.dialogService = dialogService;
49
+ this.renderer = renderer;
50
+ button.showText = 'both';
51
+ }
52
+ get currentRange() {
53
+ return this.spreadsheetService.spreadsheet.options.nameBoxRef.current.value();
54
+ }
55
+ ngAfterViewInit() {
56
+ this.renderer.setAttribute(this.button.toolbarButtonElement.nativeElement, 'aria-haspopup', 'dialog');
57
+ this.renderer.setAttribute(this.button.toolbarButtonElement.nativeElement, 'aria-expanded', 'false');
58
+ }
59
+ clickHandler() {
60
+ this.openDialog();
61
+ }
62
+ /**
63
+ * @hidden
64
+ */
65
+ openDialog() {
66
+ const currentSheet = this.spreadsheetService.spreadsheet.activeSheet();
67
+ const currentRangeValidation = currentSheet.range(currentSheet.activeCell()).validation();
68
+ const dialogSettings = {
69
+ appendTo: this.spreadsheetService.dialogContainer,
70
+ title: this.localization.get(this.command),
71
+ content: DataValidationDialogComponent,
72
+ minWidth: 400,
73
+ maxHeight: '90%',
74
+ };
75
+ const dialog = this.dialogService.open(dialogSettings);
76
+ const dialogContent = dialog.content.instance;
77
+ this.renderer.setAttribute(this.button.toolbarButtonElement.nativeElement, 'aria-expanded', 'true');
78
+ let contentData;
79
+ if (currentRangeValidation) {
80
+ contentData = {
81
+ cellRange: this.currentRange,
82
+ to: currentRangeValidation.to,
83
+ from: currentRangeValidation.from,
84
+ criteria: { type: currentRangeValidation.dataType, name: CRITERIA_LIST.find(i => i.type === currentRangeValidation.dataType).name },
85
+ comparer: currentRangeValidation.dataType === 'custom' || currentRangeValidation.dataType === 'list' ? null : { type: currentRangeValidation.comparerType, name: COMPARERS_LIST.find(i => i.type === currentRangeValidation.comparerType).name },
86
+ ignoreBlank: currentRangeValidation.allowNulls,
87
+ showButton: currentRangeValidation.showButton,
88
+ onInvalidData: currentRangeValidation.type
89
+ };
90
+ if (currentRangeValidation.messageTemplate || currentRangeValidation.titleTemplate) {
91
+ contentData = Object.assign(contentData, {
92
+ hintMessage: currentRangeValidation.messageTemplate,
93
+ hintTitle: currentRangeValidation.titleTemplate,
94
+ showHint: true
95
+ });
96
+ }
97
+ }
98
+ else {
99
+ contentData = { cellRange: this.currentRange };
100
+ }
101
+ dialogContent.setData(contentData);
102
+ dialogContent.dialogAction.pipe(take(1)).subscribe((actionType) => {
103
+ if (actionType === this.localization.get('dialogCancel')) {
104
+ dialog.close();
105
+ this.renderer.setAttribute(this.button.toolbarButtonElement.nativeElement, 'aria-expanded', 'false');
106
+ }
107
+ else {
108
+ let options = {
109
+ type: dialogContent.onInvalidData,
110
+ dataType: dialogContent.criteria.type,
111
+ comparerType: dialogContent.comparer.type,
112
+ from: dialogContent.from,
113
+ to: dialogContent.to,
114
+ allowNulls: dialogContent.ignoreBlank,
115
+ showButton: dialogContent.showButton,
116
+ messageTemplate: dialogContent.showHint ? dialogContent.hintMessage : null,
117
+ titleTemplate: dialogContent.showHint ? dialogContent.hintTitle : null
118
+ };
119
+ if (dialogContent.criteria.type === 'list' || dialogContent.criteria.type === 'text') {
120
+ const isWrappedInQuotes = options.from.startsWith('"') && options.from.endsWith('"');
121
+ let isRangeValue = true;
122
+ try {
123
+ isRangeValue = Boolean(this.spreadsheetService.spreadsheet.activeSheet().range(dialogContent.from));
124
+ }
125
+ catch (e) {
126
+ isRangeValue = false;
127
+ }
128
+ if (!isWrappedInQuotes && !isRangeValue) {
129
+ options = Object.assign(options, { from: `"${options.from}"` });
130
+ }
131
+ }
132
+ if (actionType === this.localization.get('dialogRemove') || dialogContent.criteria.type === 'any') {
133
+ options = null;
134
+ }
135
+ this.spreadsheetService.spreadsheet.executeCommand({
136
+ command: 'EditValidationCommand',
137
+ options: { value: options }
138
+ });
139
+ dialog.close();
140
+ this.renderer.setAttribute(this.button.toolbarButtonElement.nativeElement, 'aria-expanded', 'false');
141
+ }
142
+ });
143
+ dialog.dialog.instance.close.pipe(take(1)).subscribe(() => {
144
+ this.renderer.setAttribute(this.button.toolbarButtonElement.nativeElement, 'aria-expanded', 'false');
145
+ });
146
+ }
147
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SpreadsheetDataValidationDirective, deps: [{ token: i1.ToolBarButtonComponent }, { token: i2.SpreadsheetLocalizationService }, { token: i3.SpreadsheetService }, { token: i4.SpreadsheetToolsService }, { token: i5.DialogService }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
148
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: SpreadsheetDataValidationDirective, isStandalone: true, selector: "kendo-toolbar-button[kendoSpreadsheetDataValidation]", usesInheritance: true, ngImport: i0 });
149
+ }
150
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SpreadsheetDataValidationDirective, decorators: [{
151
+ type: Directive,
152
+ args: [{
153
+ selector: 'kendo-toolbar-button[kendoSpreadsheetDataValidation]',
154
+ standalone: true
155
+ }]
156
+ }], ctorParameters: function () { return [{ type: i1.ToolBarButtonComponent }, { type: i2.SpreadsheetLocalizationService }, { type: i3.SpreadsheetService }, { type: i4.SpreadsheetToolsService }, { type: i5.DialogService }, { type: i0.Renderer2 }]; } });
@@ -9,6 +9,7 @@ export { SpreadsheetFontSizeComponent } from './font-size/spreadsheet-fontsize-t
9
9
  export { SpreadsheetRedoDirective } from './history/redo-tool';
10
10
  export { SpreadsheetUndoDirective } from './history/undo-tool';
11
11
  export { SpreadsheetBoldDirective } from './typographical-emphasis/bold-tool.directive';
12
+ export { SpreadsheetDataValidationDirective } from './data-validation-tool.directive';
12
13
  export { SpreadsheetItalicDirective } from './typographical-emphasis/italic-tool.directive';
13
14
  export { SpreadsheetUnderlineDirective } from './typographical-emphasis/underline-tool.directive';
14
15
  export { SpreadsheetTextAlignDirective } from './align/align-tool.directive';