@meshmakers/shared-ui 3.4.1480 → 3.5.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 +48 -0
- package/assets/octobot/lg/octo_blink.webp +0 -0
- package/assets/octobot/lg/octo_blink_still.png +0 -0
- package/assets/octobot/lg/octo_idle.webp +0 -0
- package/assets/octobot/lg/octo_idle_still.png +0 -0
- package/assets/octobot/lg/octo_look.webp +0 -0
- package/assets/octobot/lg/octo_look_still.png +0 -0
- package/assets/octobot/lg/octo_swim.webp +0 -0
- package/assets/octobot/lg/octo_swim_still.png +0 -0
- package/assets/octobot/lg/octo_thinking.webp +0 -0
- package/assets/octobot/lg/octo_thinking_still.png +0 -0
- package/assets/octobot/lg/octo_wave.webp +0 -0
- package/assets/octobot/lg/octo_wave_still.png +0 -0
- package/assets/octobot/md/octo_blink.webp +0 -0
- package/assets/octobot/md/octo_blink_still.png +0 -0
- package/assets/octobot/md/octo_idle.webp +0 -0
- package/assets/octobot/md/octo_idle_still.png +0 -0
- package/assets/octobot/md/octo_look.webp +0 -0
- package/assets/octobot/md/octo_look_still.png +0 -0
- package/assets/octobot/md/octo_swim.webp +0 -0
- package/assets/octobot/md/octo_swim_still.png +0 -0
- package/assets/octobot/md/octo_thinking.webp +0 -0
- package/assets/octobot/md/octo_thinking_still.png +0 -0
- package/assets/octobot/md/octo_wave.webp +0 -0
- package/assets/octobot/md/octo_wave_still.png +0 -0
- package/assets/octobot/sm/octo_blink.webp +0 -0
- package/assets/octobot/sm/octo_blink_still.png +0 -0
- package/assets/octobot/sm/octo_idle.webp +0 -0
- package/assets/octobot/sm/octo_idle_still.png +0 -0
- package/assets/octobot/sm/octo_look.webp +0 -0
- package/assets/octobot/sm/octo_look_still.png +0 -0
- package/assets/octobot/sm/octo_swim.webp +0 -0
- package/assets/octobot/sm/octo_swim_still.png +0 -0
- package/assets/octobot/sm/octo_thinking.webp +0 -0
- package/assets/octobot/sm/octo_thinking_still.png +0 -0
- package/assets/octobot/sm/octo_wave.webp +0 -0
- package/assets/octobot/sm/octo_wave_still.png +0 -0
- package/fesm2022/meshmakers-shared-ui-assistant-core.mjs +940 -0
- package/fesm2022/meshmakers-shared-ui-assistant-core.mjs.map +1 -0
- package/fesm2022/meshmakers-shared-ui-assistant.mjs +819 -0
- package/fesm2022/meshmakers-shared-ui-assistant.mjs.map +1 -0
- package/fesm2022/meshmakers-shared-ui-command-palette.mjs +817 -0
- package/fesm2022/meshmakers-shared-ui-command-palette.mjs.map +1 -0
- package/fesm2022/meshmakers-shared-ui-shell.mjs +1738 -0
- package/fesm2022/meshmakers-shared-ui-shell.mjs.map +1 -0
- package/fesm2022/meshmakers-shared-ui-testing.mjs +92 -0
- package/fesm2022/meshmakers-shared-ui-testing.mjs.map +1 -0
- package/fesm2022/meshmakers-shared-ui.mjs +1550 -203
- package/fesm2022/meshmakers-shared-ui.mjs.map +1 -1
- package/package.json +28 -2
- package/types/meshmakers-shared-ui-assistant-core.d.ts +732 -0
- package/types/meshmakers-shared-ui-assistant.d.ts +258 -0
- package/types/meshmakers-shared-ui-command-palette.d.ts +398 -0
- package/types/meshmakers-shared-ui-shell.d.ts +939 -0
- package/types/meshmakers-shared-ui-testing.d.ts +41 -0
- package/types/meshmakers-shared-ui.d.ts +1004 -154
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, signal, ViewChild, Input, ChangeDetectionStrategy, Component, Injectable, NgZone, TemplateRef, Directive, Pipe, EventEmitter, ChangeDetectorRef,
|
|
3
|
-
import * as i3$
|
|
2
|
+
import { inject, signal, ViewChild, Input, ChangeDetectionStrategy, Component, Injectable, ElementRef, InjectionToken, computed, NgZone, TemplateRef, Directive, Pipe, EventEmitter, ChangeDetectorRef, input, booleanAttribute, output, ViewEncapsulation, DestroyRef, effect, PLATFORM_ID, Output, HostBinding, ContentChild, HostListener, forwardRef, numberAttribute, makeEnvironmentProviders } from '@angular/core';
|
|
3
|
+
import * as i3$1 from '@progress/kendo-angular-dialog';
|
|
4
4
|
import { DialogContentBase, DialogRef, DialogActionsComponent, DialogService, WindowRef, WindowService, WindowModule, WindowCloseResult, DialogModule } from '@progress/kendo-angular-dialog';
|
|
5
5
|
import { NotificationService } from '@progress/kendo-angular-notification';
|
|
6
|
-
import * as
|
|
7
|
-
import { ButtonComponent, ButtonModule, DropDownButtonComponent, SplitButtonComponent, ButtonsModule, ButtonGroupModule } from '@progress/kendo-angular-buttons';
|
|
8
|
-
import * as i1$
|
|
9
|
-
import { NgIf, CommonModule, NgComponentOutlet, NgTemplateOutlet, DatePipe, DecimalPipe, AsyncPipe, NgClass } from '@angular/common';
|
|
10
|
-
import * as
|
|
6
|
+
import * as i1$1 from '@progress/kendo-angular-buttons';
|
|
7
|
+
import { ButtonComponent, ButtonModule, DropDownButtonComponent, SplitButtonComponent, ButtonsModule, ButtonGroupModule, DropDownButtonModule } from '@progress/kendo-angular-buttons';
|
|
8
|
+
import * as i1$3 from '@angular/common';
|
|
9
|
+
import { NgIf, CommonModule, isPlatformBrowser, NgComponentOutlet, NgTemplateOutlet, DatePipe, DecimalPipe, AsyncPipe, NgClass } from '@angular/common';
|
|
10
|
+
import * as i2 from '@progress/kendo-angular-icons';
|
|
11
11
|
import { SVGIconComponent, SVGIconModule, IconsModule } from '@progress/kendo-angular-icons';
|
|
12
12
|
import { firstValueFrom, fromEvent, BehaviorSubject, of, Subject, asyncScheduler, from, Subscription } from 'rxjs';
|
|
13
13
|
import * as i1 from '@angular/forms';
|
|
@@ -17,26 +17,26 @@ import { LabelComponent, LabelModule } from '@progress/kendo-angular-label';
|
|
|
17
17
|
import * as i5 from '@progress/kendo-angular-inputs';
|
|
18
18
|
import { TextBoxComponent, CheckBoxComponent, SeparatorComponent, NumericTextBoxComponent, InputsModule, NumericTextBoxModule } from '@progress/kendo-angular-inputs';
|
|
19
19
|
import { ProgressBarComponent } from '@progress/kendo-angular-progressbar';
|
|
20
|
-
import { copyIcon, xIcon, filterIcon, filePdfIcon, fileExcelIcon,
|
|
21
|
-
import { Router, NavigationEnd } from '@angular/router';
|
|
20
|
+
import { copyIcon, xIcon, calculatorIcon, clockArrowRotateIcon, passwordIcon, cellsMergeIcon, unpinIcon, pinIcon, undoIcon, fileReportIcon, downloadIcon, importIcon, exportIcon, stopIcon, playIcon, xCircleIcon, uploadIcon, arrowRotateCwIcon, fileDataIcon, eraserIcon, trashIcon, hyperlinkOpenIcon, eyeIcon, pencilIcon, plusIcon, checkIcon, filterIcon, filePdfIcon, fileExcelIcon, filterClearIcon, moreVerticalIcon, slidersIcon, saveIcon, cancelIcon, searchIcon, clockIcon } from '@progress/kendo-svg-icons';
|
|
21
|
+
import { Router, NavigationEnd, RouterLink, ActivatedRoute } from '@angular/router';
|
|
22
22
|
import { filter, debounceTime, distinctUntilChanged, takeUntil, observeOn, tap, switchMap, catchError } from 'rxjs/operators';
|
|
23
23
|
import { MessageService, CommandBaseService, CommandSettingsService, provideMmSharedServices } from '@meshmakers/shared-services';
|
|
24
|
-
import * as i1$
|
|
25
|
-
import { DataBindingDirective, GridComponent, ColumnComponent, ToolbarTemplateDirective, GridSpacerComponent, ExcelModule, PDFModule, CommandColumnComponent, CellTemplateDirective, CheckboxColumnComponent, CustomMessagesComponent, FilterCellTemplateDirective, StringFilterCellComponent, NumericFilterCellComponent, BooleanFilterCellComponent, DateFilterCellComponent } from '@progress/kendo-angular-grid';
|
|
26
|
-
import * as i3
|
|
24
|
+
import * as i1$2 from '@progress/kendo-angular-grid';
|
|
25
|
+
import { DataBindingDirective, GridComponent, ColumnComponent, ToolbarTemplateDirective, GridSpacerComponent, ExcelModule, PDFModule, CommandColumnComponent, CellTemplateDirective, CheckboxColumnComponent, CustomMessagesComponent, FilterCellTemplateDirective, StringFilterCellComponent, NumericFilterCellComponent, BooleanFilterCellComponent, DateFilterCellComponent, NoRecordsTemplateDirective } from '@progress/kendo-angular-grid';
|
|
26
|
+
import * as i3 from '@progress/kendo-angular-dropdowns';
|
|
27
27
|
import { DropDownListComponent, ValueTemplateDirective, ItemTemplateDirective, AutoCompleteModule, DropDownListModule } from '@progress/kendo-angular-dropdowns';
|
|
28
28
|
import { isCompositeFilterDescriptor } from '@progress/kendo-data-query';
|
|
29
29
|
import { ContextMenuComponent, HierarchyBindingDirective } from '@progress/kendo-angular-menu';
|
|
30
30
|
import cronstrue from 'cronstrue';
|
|
31
31
|
import 'cronstrue/locales/de.js';
|
|
32
32
|
import 'cronstrue/locales/en.js';
|
|
33
|
-
import * as i2 from '@progress/kendo-angular-excel-export';
|
|
33
|
+
import * as i2$1 from '@progress/kendo-angular-excel-export';
|
|
34
34
|
import { DropPosition, ExpandDirective, TreeViewComponent, NodeTemplateDirective, DragAndDropDirective } from '@progress/kendo-angular-treeview';
|
|
35
|
-
import * as i1$
|
|
35
|
+
import * as i1$4 from '@progress/kendo-angular-layout';
|
|
36
36
|
import { SplitterComponent, SplitterPaneComponent, TabStripModule } from '@progress/kendo-angular-layout';
|
|
37
37
|
import * as i7 from '@progress/kendo-angular-indicators';
|
|
38
38
|
import { LoaderModule, IndicatorsModule } from '@progress/kendo-angular-indicators';
|
|
39
|
-
import * as i2$
|
|
39
|
+
import * as i2$2 from '@progress/kendo-angular-dateinputs';
|
|
40
40
|
import { DatePickerModule, DateTimePickerModule } from '@progress/kendo-angular-dateinputs';
|
|
41
41
|
import { CronExpressionParser } from 'cron-parser';
|
|
42
42
|
|
|
@@ -280,6 +280,20 @@ class ConfirmationWindowComponent extends DialogContentBase {
|
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
282
|
}
|
|
283
|
+
host = inject((ElementRef));
|
|
284
|
+
/**
|
|
285
|
+
* Destructive confirmations start with the focus on the dismissing button (AB#5578): Enter must
|
|
286
|
+
* never confirm a delete. Backs up the service's `autoFocusedElement`.
|
|
287
|
+
*/
|
|
288
|
+
ngAfterViewInit() {
|
|
289
|
+
super.ngAfterViewInit();
|
|
290
|
+
if (this.data?.danger) {
|
|
291
|
+
setTimeout(() => {
|
|
292
|
+
const root = this.host.nativeElement.closest('.k-dialog') ?? this.host.nativeElement;
|
|
293
|
+
root.querySelector('[data-action="dismiss"], [data-action="cancel"]')?.focus();
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
283
297
|
onButton1() {
|
|
284
298
|
this.dialogRef.close((new ConfirmationWindowResult(this.button1Result)));
|
|
285
299
|
}
|
|
@@ -290,7 +304,7 @@ class ConfirmationWindowComponent extends DialogContentBase {
|
|
|
290
304
|
this.dialogRef.close((new ConfirmationWindowResult(this.button3Result)));
|
|
291
305
|
}
|
|
292
306
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ConfirmationWindowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
293
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.5", type: ConfirmationWindowComponent, isStandalone: true, selector: "mm-confirmation-window", inputs: { data: "data" }, usesInheritance: true, ngImport: i0, template: "<div>{{data?.message}}</div>\n<kendo-dialog-actions>\n <button kendoButton (click)=\"
|
|
307
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.5", type: ConfirmationWindowComponent, isStandalone: true, selector: "mm-confirmation-window", inputs: { data: "data" }, usesInheritance: true, ngImport: i0, template: "<div>{{data?.message}}</div>\n<!-- Button order (Studio action guideline \u00A72.2, app convention): dismissing buttons first (left),\n the confirming / primary button last (right) - also for destructive confirmations. -->\n<kendo-dialog-actions>\n <button kendoButton (click)=\"onButton3()\" *ngIf=\"button3Text\" data-action=\"cancel\">{{button3Text}}</button>\n <button kendoButton (click)=\"onButton2()\" *ngIf=\"button2Text\" data-action=\"dismiss\">{{button2Text}}</button>\n <button kendoButton (click)=\"onButton1()\" [themeColor]=\"data?.danger ? 'error' : 'primary'\" [attr.data-danger]=\"data?.danger ? 'true' : null\" data-action=\"confirm\">{{button1Text}}</button>\n</kendo-dialog-actions>\n", styles: [""], dependencies: [{ kind: "component", type: DialogActionsComponent, selector: "kendo-dialog-actions", inputs: ["actions", "layout"], outputs: ["action"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
294
308
|
}
|
|
295
309
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ConfirmationWindowComponent, decorators: [{
|
|
296
310
|
type: Component,
|
|
@@ -298,14 +312,232 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
298
312
|
DialogActionsComponent,
|
|
299
313
|
NgIf,
|
|
300
314
|
ButtonComponent
|
|
301
|
-
], changeDetection: ChangeDetectionStrategy.Eager, template: "<div>{{data?.message}}</div>\n<kendo-dialog-actions>\n <button kendoButton (click)=\"
|
|
315
|
+
], changeDetection: ChangeDetectionStrategy.Eager, template: "<div>{{data?.message}}</div>\n<!-- Button order (Studio action guideline \u00A72.2, app convention): dismissing buttons first (left),\n the confirming / primary button last (right) - also for destructive confirmations. -->\n<kendo-dialog-actions>\n <button kendoButton (click)=\"onButton3()\" *ngIf=\"button3Text\" data-action=\"cancel\">{{button3Text}}</button>\n <button kendoButton (click)=\"onButton2()\" *ngIf=\"button2Text\" data-action=\"dismiss\">{{button2Text}}</button>\n <button kendoButton (click)=\"onButton1()\" [themeColor]=\"data?.danger ? 'error' : 'primary'\" [attr.data-danger]=\"data?.danger ? 'true' : null\" data-action=\"confirm\">{{button1Text}}</button>\n</kendo-dialog-actions>\n" }]
|
|
302
316
|
}], ctorParameters: () => [], propDecorators: { data: [{
|
|
303
317
|
type: Input
|
|
304
318
|
}] } });
|
|
305
319
|
|
|
320
|
+
const DEFAULT_DANGER_CONFIRMATION_MESSAGES = {
|
|
321
|
+
cancel: 'Cancel',
|
|
322
|
+
typeToConfirm: 'Type {name} to confirm',
|
|
323
|
+
environmentNotice: '{environment} environment. Check the target before you confirm.',
|
|
324
|
+
target: 'Target',
|
|
325
|
+
};
|
|
326
|
+
/** Word typed when the target name is empty and no confirm text is set either. */
|
|
327
|
+
const DANGER_CONFIRM_FALLBACK_WORD = 'DELETE';
|
|
328
|
+
/**
|
|
329
|
+
* The text the user has to type when `requireTypingName` is set: the target name, or — when the
|
|
330
|
+
* target name is empty/whitespace — the confirm button text, else {@link DANGER_CONFIRM_FALLBACK_WORD}.
|
|
331
|
+
* Never empty, so an empty input can never satisfy the type-to-confirm rule.
|
|
332
|
+
*/
|
|
333
|
+
function dangerConfirmTypingToken(options) {
|
|
334
|
+
return (options.targetName ?? '').trim() || (options.confirmText ?? '').trim() || DANGER_CONFIRM_FALLBACK_WORD;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* True when `typed` matches the target name (trimmed, case-sensitive). An empty/whitespace target
|
|
338
|
+
* name never matches — use {@link dangerConfirmTypingToken} to get a non-empty word to type.
|
|
339
|
+
*/
|
|
340
|
+
function dangerConfirmNameMatches(typed, targetName) {
|
|
341
|
+
const expected = (targetName ?? '').trim();
|
|
342
|
+
return expected.length > 0 && (typed ?? '').trim() === expected;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Optional hook providing the current {@link DangerConfirmEnvironment}; evaluated each time a
|
|
346
|
+
* danger confirmation opens (so it may read signals). Absent = behaviour unchanged.
|
|
347
|
+
*
|
|
348
|
+
* ```ts
|
|
349
|
+
* { provide: DANGER_CONFIRM_ENVIRONMENT, useFactory: () => {
|
|
350
|
+
* const mode = inject(MyEnvironmentService);
|
|
351
|
+
* return () => ({ requireTypingName: mode.isProduction(), environmentLabel: mode.label() });
|
|
352
|
+
* } }
|
|
353
|
+
* ```
|
|
354
|
+
*/
|
|
355
|
+
const DANGER_CONFIRM_ENVIRONMENT = new InjectionToken('DANGER_CONFIRM_ENVIRONMENT');
|
|
356
|
+
/** Applies the environment defaults to options that leave `requireTypingName` / `environmentLabel` unset. */
|
|
357
|
+
function applyDangerConfirmEnvironment(options, environment) {
|
|
358
|
+
if (!environment) {
|
|
359
|
+
return options;
|
|
360
|
+
}
|
|
361
|
+
return {
|
|
362
|
+
...options,
|
|
363
|
+
requireTypingName: options.requireTypingName ?? environment.requireTypingName,
|
|
364
|
+
environmentLabel: options.environmentLabel !== undefined ? options.environmentLabel : (environment.environmentLabel ?? null),
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
let nextId = 0;
|
|
369
|
+
/** Selector of the element that gets the initial focus (never the destructive button). */
|
|
370
|
+
function DANGER_CONFIRM_SAFE_FOCUS(requireTypingName) {
|
|
371
|
+
return requireTypingName ? '[data-type-to-confirm]' : '[data-action="cancel"]';
|
|
372
|
+
}
|
|
373
|
+
/** Result of the danger confirmation dialog: `true` only for the confirming button. */
|
|
374
|
+
class DangerConfirmationResult {
|
|
375
|
+
confirmed;
|
|
376
|
+
constructor(confirmed) {
|
|
377
|
+
this.confirmed = confirmed;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Content of {@link ConfirmationService.showDangerConfirm} (AB#5578): environment notice,
|
|
382
|
+
* target, consequence, optional type-to-confirm input; Cancel left, danger confirm right.
|
|
383
|
+
*/
|
|
384
|
+
class DangerConfirmationWindowComponent extends DialogContentBase {
|
|
385
|
+
ref;
|
|
386
|
+
/** Set by the service right after opening. */
|
|
387
|
+
options = signal({ title: '', targetName: '', consequence: '', confirmText: '' }, /* @ts-ignore */
|
|
388
|
+
...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
389
|
+
typed = signal('', /* @ts-ignore */
|
|
390
|
+
...(ngDevMode ? [{ debugName: "typed" }] : /* istanbul ignore next */ []));
|
|
391
|
+
inputId = `mm-danger-confirm-${nextId++}`;
|
|
392
|
+
messages = computed(() => ({
|
|
393
|
+
...DEFAULT_DANGER_CONFIRMATION_MESSAGES,
|
|
394
|
+
...(this.options().messages ?? {}),
|
|
395
|
+
}), /* @ts-ignore */
|
|
396
|
+
...(ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []));
|
|
397
|
+
/** What has to be typed: the target name, or a non-empty fallback when it is empty. */
|
|
398
|
+
typingToken = computed(() => dangerConfirmTypingToken(this.options()), /* @ts-ignore */
|
|
399
|
+
...(ngDevMode ? [{ debugName: "typingToken" }] : /* istanbul ignore next */ []));
|
|
400
|
+
typeLabel = computed(() => this.messages().typeToConfirm.replace('{name}', this.typingToken()), /* @ts-ignore */
|
|
401
|
+
...(ngDevMode ? [{ debugName: "typeLabel" }] : /* istanbul ignore next */ []));
|
|
402
|
+
environmentNotice = computed(() => this.messages().environmentNotice.replace('{environment}', this.options().environmentLabel ?? ''), /* @ts-ignore */
|
|
403
|
+
...(ngDevMode ? [{ debugName: "environmentNotice" }] : /* istanbul ignore next */ []));
|
|
404
|
+
canConfirm = computed(() => !this.options().requireTypingName || dangerConfirmNameMatches(this.typed(), this.typingToken()), /* @ts-ignore */
|
|
405
|
+
...(ngDevMode ? [{ debugName: "canConfirm" }] : /* istanbul ignore next */ []));
|
|
406
|
+
constructor() {
|
|
407
|
+
const ref = inject(DialogRef);
|
|
408
|
+
super(ref);
|
|
409
|
+
this.ref = ref;
|
|
410
|
+
}
|
|
411
|
+
host = inject((ElementRef));
|
|
412
|
+
/**
|
|
413
|
+
* Initial focus never on the destructive button (Enter must not delete): the type-to-confirm
|
|
414
|
+
* input when required, else Cancel. The service also passes `autoFocusedElement`; this is the
|
|
415
|
+
* fallback for hosts that render the content themselves.
|
|
416
|
+
*/
|
|
417
|
+
ngAfterViewInit() {
|
|
418
|
+
super.ngAfterViewInit();
|
|
419
|
+
setTimeout(() => this.focusSafeElement());
|
|
420
|
+
}
|
|
421
|
+
/** Focuses the type-to-confirm input or Cancel (unless focus already sits on one of them). */
|
|
422
|
+
focusSafeElement() {
|
|
423
|
+
const root = this.host.nativeElement.closest('.k-dialog') ?? this.host.nativeElement;
|
|
424
|
+
const target = root.querySelector(DANGER_CONFIRM_SAFE_FOCUS(!!this.options().requireTypingName));
|
|
425
|
+
target?.focus();
|
|
426
|
+
}
|
|
427
|
+
cancel() {
|
|
428
|
+
this.ref.close(new DangerConfirmationResult(false));
|
|
429
|
+
}
|
|
430
|
+
confirm() {
|
|
431
|
+
if (this.canConfirm()) {
|
|
432
|
+
this.ref.close(new DangerConfirmationResult(true));
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: DangerConfirmationWindowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
436
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: DangerConfirmationWindowComponent, isStandalone: true, selector: "mm-danger-confirmation-window", usesInheritance: true, ngImport: i0, template: `
|
|
437
|
+
@let o = options();
|
|
438
|
+
<div class="mm-danger-confirmation" data-dialog="danger-confirm">
|
|
439
|
+
@if (o.environmentLabel) {
|
|
440
|
+
<p class="mm-danger-confirmation__environment" role="alert" data-production-warning>
|
|
441
|
+
{{ environmentNotice() }}
|
|
442
|
+
</p>
|
|
443
|
+
}
|
|
444
|
+
<p class="mm-danger-confirmation__target">
|
|
445
|
+
<span class="mm-danger-confirmation__target-label">{{ messages().target }}:</span>
|
|
446
|
+
<strong data-target>{{ o.targetName }}</strong>
|
|
447
|
+
</p>
|
|
448
|
+
<p class="mm-danger-confirmation__consequence" data-consequence>{{ o.consequence }}</p>
|
|
449
|
+
@if (o.requireTypingName) {
|
|
450
|
+
<label class="mm-danger-confirmation__type" [for]="inputId">{{ typeLabel() }}</label>
|
|
451
|
+
<input
|
|
452
|
+
class="k-textbox k-input k-input-md k-rounded-md mm-danger-confirmation__input"
|
|
453
|
+
[id]="inputId"
|
|
454
|
+
type="text"
|
|
455
|
+
autocomplete="off"
|
|
456
|
+
spellcheck="false"
|
|
457
|
+
data-type-to-confirm
|
|
458
|
+
[value]="typed()"
|
|
459
|
+
(input)="typed.set($any($event.target).value)"
|
|
460
|
+
(keydown.enter)="canConfirm() && confirm()"
|
|
461
|
+
/>
|
|
462
|
+
}
|
|
463
|
+
</div>
|
|
464
|
+
<kendo-dialog-actions>
|
|
465
|
+
<button kendoButton data-action="cancel" (click)="cancel()">{{ o.cancelText ?? messages().cancel }}</button>
|
|
466
|
+
<button kendoButton themeColor="error" data-action="confirm" data-danger="true"
|
|
467
|
+
[disabled]="!canConfirm()" (click)="confirm()">{{ o.confirmText }}</button>
|
|
468
|
+
</kendo-dialog-actions>
|
|
469
|
+
`, isInline: true, styles: [".mm-danger-confirmation p{margin:0 0 var(--theme-space-3, 12px)}.mm-danger-confirmation__environment{padding:var(--theme-space-2, 8px) var(--theme-space-3, 12px);border-left:3px solid var(--theme-status-error, #c0385f);border-radius:var(--theme-radius-sm, 4px);background:var(--theme-status-error-subtle, rgba(192, 56, 95, .08));font-weight:600}.mm-danger-confirmation__target-label{color:var(--theme-text-muted, #75829a);margin-right:var(--theme-space-1, 4px)}.mm-danger-confirmation__type{display:block;margin-bottom:var(--theme-space-1, 4px)}.mm-danger-confirmation__input{width:100%}\n"], dependencies: [{ 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", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
470
|
+
}
|
|
471
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: DangerConfirmationWindowComponent, decorators: [{
|
|
472
|
+
type: Component,
|
|
473
|
+
args: [{ selector: 'mm-danger-confirmation-window', standalone: true, imports: [DialogActionsComponent, ButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
474
|
+
@let o = options();
|
|
475
|
+
<div class="mm-danger-confirmation" data-dialog="danger-confirm">
|
|
476
|
+
@if (o.environmentLabel) {
|
|
477
|
+
<p class="mm-danger-confirmation__environment" role="alert" data-production-warning>
|
|
478
|
+
{{ environmentNotice() }}
|
|
479
|
+
</p>
|
|
480
|
+
}
|
|
481
|
+
<p class="mm-danger-confirmation__target">
|
|
482
|
+
<span class="mm-danger-confirmation__target-label">{{ messages().target }}:</span>
|
|
483
|
+
<strong data-target>{{ o.targetName }}</strong>
|
|
484
|
+
</p>
|
|
485
|
+
<p class="mm-danger-confirmation__consequence" data-consequence>{{ o.consequence }}</p>
|
|
486
|
+
@if (o.requireTypingName) {
|
|
487
|
+
<label class="mm-danger-confirmation__type" [for]="inputId">{{ typeLabel() }}</label>
|
|
488
|
+
<input
|
|
489
|
+
class="k-textbox k-input k-input-md k-rounded-md mm-danger-confirmation__input"
|
|
490
|
+
[id]="inputId"
|
|
491
|
+
type="text"
|
|
492
|
+
autocomplete="off"
|
|
493
|
+
spellcheck="false"
|
|
494
|
+
data-type-to-confirm
|
|
495
|
+
[value]="typed()"
|
|
496
|
+
(input)="typed.set($any($event.target).value)"
|
|
497
|
+
(keydown.enter)="canConfirm() && confirm()"
|
|
498
|
+
/>
|
|
499
|
+
}
|
|
500
|
+
</div>
|
|
501
|
+
<kendo-dialog-actions>
|
|
502
|
+
<button kendoButton data-action="cancel" (click)="cancel()">{{ o.cancelText ?? messages().cancel }}</button>
|
|
503
|
+
<button kendoButton themeColor="error" data-action="confirm" data-danger="true"
|
|
504
|
+
[disabled]="!canConfirm()" (click)="confirm()">{{ o.confirmText }}</button>
|
|
505
|
+
</kendo-dialog-actions>
|
|
506
|
+
`, styles: [".mm-danger-confirmation p{margin:0 0 var(--theme-space-3, 12px)}.mm-danger-confirmation__environment{padding:var(--theme-space-2, 8px) var(--theme-space-3, 12px);border-left:3px solid var(--theme-status-error, #c0385f);border-radius:var(--theme-radius-sm, 4px);background:var(--theme-status-error-subtle, rgba(192, 56, 95, .08));font-weight:600}.mm-danger-confirmation__target-label{color:var(--theme-text-muted, #75829a);margin-right:var(--theme-space-1, 4px)}.mm-danger-confirmation__type{display:block;margin-bottom:var(--theme-space-1, 4px)}.mm-danger-confirmation__input{width:100%}\n"] }]
|
|
507
|
+
}], ctorParameters: () => [] });
|
|
508
|
+
|
|
306
509
|
class ConfirmationService {
|
|
307
510
|
dialogService = inject(DialogService);
|
|
511
|
+
/** Optional app-wide environment defaults (e.g. production → type the name), AB#5578. */
|
|
512
|
+
dangerEnvironment = inject(DANGER_CONFIRM_ENVIRONMENT, { optional: true });
|
|
308
513
|
defaultMessages;
|
|
514
|
+
/** App-wide translations of {@link showDangerConfirm}; per-call `options.messages` win. */
|
|
515
|
+
defaultDangerMessages;
|
|
516
|
+
/**
|
|
517
|
+
* Danger confirmation that names the target (Studio action guideline §2.4, AB#5578): title +
|
|
518
|
+
* target + consequence, Cancel left / danger confirm right, initial focus on Cancel (or on the
|
|
519
|
+
* type-to-confirm input when `requireTypingName`, e.g. in production tenants). Resolves `true`
|
|
520
|
+
* only when the confirming button was used. Unset `requireTypingName` / `environmentLabel` fall
|
|
521
|
+
* back to the app's {@link DANGER_CONFIRM_ENVIRONMENT}, if provided.
|
|
522
|
+
*/
|
|
523
|
+
async showDangerConfirm(callerOptions) {
|
|
524
|
+
const options = applyDangerConfirmEnvironment(callerOptions, this.dangerEnvironment?.());
|
|
525
|
+
const dialogRef = this.dialogService.open({
|
|
526
|
+
title: options.title,
|
|
527
|
+
content: DangerConfirmationWindowComponent,
|
|
528
|
+
cssClass: 'mm-dialog-danger',
|
|
529
|
+
autoFocusedElement: DANGER_CONFIRM_SAFE_FOCUS(!!options.requireTypingName),
|
|
530
|
+
minWidth: 'min(320px, calc(100vw - 32px))',
|
|
531
|
+
maxWidth: 'min(560px, calc(100vw - 32px))',
|
|
532
|
+
});
|
|
533
|
+
const component = dialogRef.content.instance;
|
|
534
|
+
component.options.set({
|
|
535
|
+
...options,
|
|
536
|
+
messages: { ...(this.defaultDangerMessages ?? {}), ...(options.messages ?? {}) },
|
|
537
|
+
});
|
|
538
|
+
const result = await firstValueFrom(dialogRef.result);
|
|
539
|
+
return result instanceof DangerConfirmationResult && result.confirmed;
|
|
540
|
+
}
|
|
309
541
|
async showYesNoConfirmationDialog(title, message, cssClass, buttonLabels, messages) {
|
|
310
542
|
const dialogRef = this.openDialog(title, message, DialogType.YesNo, cssClass, buttonLabels, messages);
|
|
311
543
|
const result = await firstValueFrom(dialogRef.result);
|
|
@@ -341,6 +573,16 @@ class ConfirmationService {
|
|
|
341
573
|
return false;
|
|
342
574
|
}
|
|
343
575
|
}
|
|
576
|
+
/**
|
|
577
|
+
* Confirmation of a destructive action: explicit verb labels instead of Yes / No (e.g.
|
|
578
|
+
* "Rotate" / "Cancel") and the confirming button styled as danger. Resolves `true` only when
|
|
579
|
+
* the confirming button was clicked.
|
|
580
|
+
*/
|
|
581
|
+
async showDestructiveConfirmationDialog(title, message, confirmLabel, cancelLabel = 'Cancel') {
|
|
582
|
+
const dialogRef = this.openDialog(title, message, DialogType.YesNo, undefined, { yes: confirmLabel, no: cancelLabel }, undefined, true);
|
|
583
|
+
const result = await firstValueFrom(dialogRef.result);
|
|
584
|
+
return result instanceof ConfirmationWindowResult && result.result === ButtonTypes.Yes;
|
|
585
|
+
}
|
|
344
586
|
async showOkDialog(title, message, messages) {
|
|
345
587
|
const dialogRef = this.openDialog(title, message, DialogType.Ok, undefined, undefined, messages);
|
|
346
588
|
const result = await firstValueFrom(dialogRef.result);
|
|
@@ -351,11 +593,19 @@ class ConfirmationService {
|
|
|
351
593
|
return false;
|
|
352
594
|
}
|
|
353
595
|
}
|
|
354
|
-
openDialog(title, message, dialogType, cssClass, buttonLabels, messages) {
|
|
596
|
+
openDialog(title, message, dialogType, cssClass, buttonLabels, messages, danger = false) {
|
|
597
|
+
// A confirmation is a short text: cap its width so a long message wraps instead of
|
|
598
|
+
// stretching the dialog over the whole viewport.
|
|
355
599
|
const dialogRef = this.dialogService.open({
|
|
356
600
|
title,
|
|
357
601
|
content: ConfirmationWindowComponent,
|
|
358
|
-
cssClass
|
|
602
|
+
cssClass,
|
|
603
|
+
// Destructive confirmations focus the dismissing button first: Enter must never confirm a
|
|
604
|
+
// delete (AB#5578). Danger = showDestructiveConfirmationDialog, 'mm-dialog-danger' = production check.
|
|
605
|
+
...(danger || cssClass?.includes('mm-dialog-danger') ? { autoFocusedElement: '[data-action="dismiss"], [data-action="cancel"]' } : {}),
|
|
606
|
+
// Both bounds leave a 16 px gutter on each side, so a phone (< 352 px) never overflows.
|
|
607
|
+
minWidth: 'min(320px, calc(100vw - 32px))',
|
|
608
|
+
maxWidth: 'min(560px, calc(100vw - 32px))'
|
|
359
609
|
});
|
|
360
610
|
const component = dialogRef.content.instance;
|
|
361
611
|
component.data = {
|
|
@@ -364,6 +614,7 @@ class ConfirmationService {
|
|
|
364
614
|
dialogType,
|
|
365
615
|
buttonLabels,
|
|
366
616
|
messages: messages ?? this.defaultMessages,
|
|
617
|
+
...(danger ? { danger: true } : {}),
|
|
367
618
|
};
|
|
368
619
|
return dialogRef;
|
|
369
620
|
}
|
|
@@ -682,7 +933,7 @@ class MessageDetailsDialogComponent {
|
|
|
682
933
|
</button>
|
|
683
934
|
</div>
|
|
684
935
|
</div>
|
|
685
|
-
`, isInline: true, styles: [".message-details-content{display:flex;flex-direction:column;height:100%;padding:16px;box-sizing:border-box;overflow:hidden}.loading-section{flex:1;display:flex;align-items:center;justify-content:center}.details-pre{flex:1;margin:0;font-family:Courier New,monospace;font-size:13px;line-height:1.5;border:1px solid var(--kendo-color-border);background:var(--kendo-color-base-subtle);border-radius:4px;padding:12px;overflow:scroll;white-space:pre;min-height:0}.details-pre::-webkit-scrollbar{width:10px;height:10px}.details-pre::-webkit-scrollbar-track{background:var(--kendo-color-base-subtle, #f5f5f5);border-radius:4px}.details-pre::-webkit-scrollbar-thumb{background:var(--kendo-color-border, #ccc);border-radius:4px}.details-pre::-webkit-scrollbar-thumb:hover{background:#999}.dialog-actions{flex-shrink:0;padding-top:16px;margin-top:16px;border-top:1px solid var(--kendo-color-border);display:flex;flex-direction:row;justify-content:space-between;gap:12px}\n"], dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type:
|
|
936
|
+
`, isInline: true, styles: [".message-details-content{display:flex;flex-direction:column;height:100%;padding:16px;box-sizing:border-box;overflow:hidden}.loading-section{flex:1;display:flex;align-items:center;justify-content:center}.details-pre{flex:1;margin:0;font-family:Courier New,monospace;font-size:13px;line-height:1.5;border:1px solid var(--kendo-color-border);background:var(--kendo-color-base-subtle);border-radius:4px;padding:12px;overflow:scroll;white-space:pre;min-height:0}.details-pre::-webkit-scrollbar{width:10px;height:10px}.details-pre::-webkit-scrollbar-track{background:var(--kendo-color-base-subtle, #f5f5f5);border-radius:4px}.details-pre::-webkit-scrollbar-thumb{background:var(--kendo-color-border, #ccc);border-radius:4px}.details-pre::-webkit-scrollbar-thumb:hover{background:#999}.dialog-actions{flex-shrink:0;padding-top:16px;margin-top:16px;border-top:1px solid var(--kendo-color-border);display:flex;flex-direction:row;justify-content:space-between;gap:12px}\n"], dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
686
937
|
}
|
|
687
938
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: MessageDetailsDialogComponent, decorators: [{
|
|
688
939
|
type: Component,
|
|
@@ -944,13 +1195,29 @@ const DEFAULT_NOTIFICATION_DISPLAY_MESSAGES = {
|
|
|
944
1195
|
showDetails: 'Show Details',
|
|
945
1196
|
};
|
|
946
1197
|
|
|
1198
|
+
const NOTIFICATION_DISPLAY_OPTIONS = new InjectionToken('NOTIFICATION_DISPLAY_OPTIONS');
|
|
1199
|
+
const DEFAULT_OPTIONS = {
|
|
1200
|
+
errorHideAfter: 0,
|
|
1201
|
+
warningHideAfter: 0,
|
|
1202
|
+
maxVisible: 0,
|
|
1203
|
+
dedupe: true,
|
|
1204
|
+
stickyWithDetails: false,
|
|
1205
|
+
};
|
|
1206
|
+
/** Eviction order of the stack cap: lower goes first. */
|
|
1207
|
+
const SEVERITY = { success: 0, info: 0, warning: 1, error: 2 };
|
|
947
1208
|
class NotificationDisplayService {
|
|
948
1209
|
notificationService = inject(NotificationService);
|
|
949
1210
|
messageDetailsDialogService = inject(MessageDetailsDialogService);
|
|
950
1211
|
router = inject(Router);
|
|
951
1212
|
ngZone = inject(NgZone);
|
|
1213
|
+
options = {
|
|
1214
|
+
...DEFAULT_OPTIONS,
|
|
1215
|
+
...(inject(NOTIFICATION_DISPLAY_OPTIONS, { optional: true }) ?? {}),
|
|
1216
|
+
};
|
|
952
1217
|
notificationCounter = 0;
|
|
953
1218
|
activeNotifications = new Map();
|
|
1219
|
+
/** Every visible toast, oldest first (dedupe, stack cap, auto-hide timers). */
|
|
1220
|
+
toasts = [];
|
|
954
1221
|
subscriptions = [];
|
|
955
1222
|
interactionDebounceTime = 300; // ms
|
|
956
1223
|
gracePeriod = 1000; // ms - prevent immediate closure after opening
|
|
@@ -1051,6 +1318,7 @@ class NotificationDisplayService {
|
|
|
1051
1318
|
toRemove.forEach(id => {
|
|
1052
1319
|
this.activeNotifications.delete(id);
|
|
1053
1320
|
this.notificationOpenTimes.delete(id);
|
|
1321
|
+
this.forget(id);
|
|
1054
1322
|
});
|
|
1055
1323
|
}
|
|
1056
1324
|
clearAllNotifications() {
|
|
@@ -1059,6 +1327,8 @@ class NotificationDisplayService {
|
|
|
1059
1327
|
}
|
|
1060
1328
|
this.activeNotifications.clear();
|
|
1061
1329
|
this.notificationOpenTimes.clear();
|
|
1330
|
+
this.toasts.forEach(toast => clearTimeout(toast.timer));
|
|
1331
|
+
this.toasts = [];
|
|
1062
1332
|
}
|
|
1063
1333
|
/**
|
|
1064
1334
|
* Helper method to create SVG icon HTML
|
|
@@ -1071,182 +1341,166 @@ class NotificationDisplayService {
|
|
|
1071
1341
|
* Shows a success notification (auto-closes after 3 seconds)
|
|
1072
1342
|
*/
|
|
1073
1343
|
showSuccess(title, hideAfter) {
|
|
1074
|
-
|
|
1075
|
-
const autoHideTime = hideAfter ?? 3000;
|
|
1076
|
-
const settings = {
|
|
1077
|
-
content: title,
|
|
1078
|
-
type: {
|
|
1079
|
-
style: 'success',
|
|
1080
|
-
icon: true
|
|
1081
|
-
},
|
|
1082
|
-
animation: { type: 'slide', duration: 400 },
|
|
1083
|
-
hideAfter: 0, // Disable Kendo's auto-hide, we'll handle it manually
|
|
1084
|
-
closable: true,
|
|
1085
|
-
position: { horizontal: 'center', vertical: 'top' }
|
|
1086
|
-
};
|
|
1087
|
-
console.log('Success notification settings:', settings);
|
|
1088
|
-
const notificationRef = this.notificationService.show(settings);
|
|
1089
|
-
console.log('Notification ref created:', notificationRef);
|
|
1090
|
-
if (notificationRef) {
|
|
1091
|
-
console.log(`Success notification will auto-hide after ${autoHideTime}ms (manual)`);
|
|
1092
|
-
// Manually hide the notification after the specified time
|
|
1093
|
-
setTimeout(() => {
|
|
1094
|
-
console.log('Manually hiding success notification');
|
|
1095
|
-
notificationRef.hide();
|
|
1096
|
-
}, autoHideTime);
|
|
1097
|
-
}
|
|
1344
|
+
this.present('success', title, undefined, hideAfter ?? 3000);
|
|
1098
1345
|
}
|
|
1099
1346
|
/**
|
|
1100
|
-
* Shows an error notification
|
|
1347
|
+
* Shows an error notification. It persists until user interaction or navigation, or hides
|
|
1348
|
+
* after `hideAfter` ms (default: `errorHideAfter` of the options; `0` = sticky).
|
|
1101
1349
|
*/
|
|
1102
1350
|
showError(title, details, hideAfter) {
|
|
1103
|
-
|
|
1104
|
-
const notificationId = `error-${++this.notificationCounter}-${Date.now()}`;
|
|
1105
|
-
// Start with plain text content
|
|
1106
|
-
const settings = {
|
|
1107
|
-
...this.defaultSettings,
|
|
1108
|
-
content: title,
|
|
1109
|
-
type: {
|
|
1110
|
-
style: 'error',
|
|
1111
|
-
icon: true
|
|
1112
|
-
},
|
|
1113
|
-
hideAfter: hideAfter ?? 0, // 0 means no auto-hide for errors
|
|
1114
|
-
closable: true,
|
|
1115
|
-
cssClass: `notification-${notificationId}` // Add unique class for targeting
|
|
1116
|
-
};
|
|
1117
|
-
const notificationRef = this.notificationService.show(settings);
|
|
1118
|
-
if (notificationRef) {
|
|
1119
|
-
this.activeNotifications.set(notificationId, notificationRef);
|
|
1120
|
-
this.notificationOpenTimes.set(notificationId, Date.now());
|
|
1121
|
-
}
|
|
1122
|
-
if (details && notificationRef) {
|
|
1123
|
-
// After notification is shown, find it by the unique class and modify its content
|
|
1124
|
-
setTimeout(() => {
|
|
1125
|
-
const notification = document.querySelector(`.notification-${notificationId}`);
|
|
1126
|
-
if (notification) {
|
|
1127
|
-
const contentEl = notification.querySelector('.k-notification-content');
|
|
1128
|
-
if (contentEl && !contentEl.querySelector('.notification-details-btn')) {
|
|
1129
|
-
// Create wrapper div
|
|
1130
|
-
const wrapper = document.createElement('div');
|
|
1131
|
-
wrapper.style.cssText = 'display: flex; align-items: center; justify-content: space-between; width: 100%; gap: 10px;';
|
|
1132
|
-
// Create text span
|
|
1133
|
-
const textSpan = document.createElement('span');
|
|
1134
|
-
textSpan.style.cssText = 'flex: 1; overflow: hidden; text-overflow: ellipsis;';
|
|
1135
|
-
textSpan.textContent = title;
|
|
1136
|
-
// Create details button with icon only
|
|
1137
|
-
const detailsBtn = document.createElement('button');
|
|
1138
|
-
detailsBtn.className = 'k-button k-button-sm k-button-flat k-button-flat-base notification-details-btn';
|
|
1139
|
-
detailsBtn.style.cssText = 'flex-shrink: 0; width: 32px; height: 32px; padding: 8px; display: flex; align-items: center; justify-content: center;';
|
|
1140
|
-
detailsBtn.innerHTML = this.createSvgIcon();
|
|
1141
|
-
detailsBtn.title = this.messages.showDetails;
|
|
1142
|
-
detailsBtn.onclick = (event) => {
|
|
1143
|
-
event.stopPropagation();
|
|
1144
|
-
this.messageDetailsDialogService.showDetailsDialog({
|
|
1145
|
-
title,
|
|
1146
|
-
details,
|
|
1147
|
-
level: 'error'
|
|
1148
|
-
});
|
|
1149
|
-
};
|
|
1150
|
-
// Assemble the elements
|
|
1151
|
-
wrapper.appendChild(textSpan);
|
|
1152
|
-
wrapper.appendChild(detailsBtn);
|
|
1153
|
-
// Replace content
|
|
1154
|
-
contentEl.innerHTML = '';
|
|
1155
|
-
contentEl.appendChild(wrapper);
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
}, 50);
|
|
1159
|
-
}
|
|
1351
|
+
this.present('error', title, details, hideAfter ?? this.options.errorHideAfter);
|
|
1160
1352
|
}
|
|
1161
1353
|
/**
|
|
1162
|
-
* Shows a warning notification
|
|
1354
|
+
* Shows a warning notification. It persists until user interaction or navigation, or hides
|
|
1355
|
+
* after `hideAfter` ms (default: `warningHideAfter` of the options; `0` = sticky).
|
|
1163
1356
|
*/
|
|
1164
1357
|
showWarning(title, details, hideAfter) {
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1358
|
+
this.present('warning', title, details, hideAfter ?? this.options.warningHideAfter);
|
|
1359
|
+
}
|
|
1360
|
+
/**
|
|
1361
|
+
* Shows an info notification (auto-closes after 3 seconds)
|
|
1362
|
+
*/
|
|
1363
|
+
showInfo(title, hideAfter) {
|
|
1364
|
+
this.present('info', title, undefined, hideAfter ?? 3000);
|
|
1365
|
+
}
|
|
1366
|
+
/** The visible toasts as `[style, text]`, oldest first — for tests and diagnostics. */
|
|
1367
|
+
visibleToasts() {
|
|
1368
|
+
return this.toasts.map(toast => [toast.style, toast.title]);
|
|
1369
|
+
}
|
|
1370
|
+
present(style, title, details, hideAfter) {
|
|
1371
|
+
const key = `${style}\u0000${title}\u0000${details ?? ''}`;
|
|
1372
|
+
if (this.options.dedupe) {
|
|
1373
|
+
const same = this.toasts.find(toast => toast.key === key);
|
|
1374
|
+
if (same) {
|
|
1375
|
+
// Already on screen: no second copy, the visible one just stays a while longer.
|
|
1376
|
+
this.schedule(same);
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
const sticky = style === 'error' || style === 'warning';
|
|
1381
|
+
// An error or warning with details is read in the details dialog: it waits for the user.
|
|
1382
|
+
const duration = sticky && details && this.options.stickyWithDetails ? 0 : hideAfter;
|
|
1383
|
+
const notificationId = `${style}-${++this.notificationCounter}-${Date.now()}`;
|
|
1168
1384
|
const settings = {
|
|
1169
1385
|
...this.defaultSettings,
|
|
1170
1386
|
content: title,
|
|
1171
|
-
type: {
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
},
|
|
1175
|
-
hideAfter: hideAfter ?? 0, // 0 means no auto-hide for warnings
|
|
1387
|
+
type: { style, icon: true },
|
|
1388
|
+
// Kendo ignores hideAfter on closable notifications: hiding is done by schedule().
|
|
1389
|
+
hideAfter: 0,
|
|
1176
1390
|
closable: true,
|
|
1177
|
-
cssClass: `notification-${notificationId}
|
|
1391
|
+
cssClass: `notification-${notificationId}`,
|
|
1178
1392
|
};
|
|
1179
1393
|
const notificationRef = this.notificationService.show(settings);
|
|
1180
|
-
if (notificationRef) {
|
|
1394
|
+
if (!notificationRef) {
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
if (sticky) {
|
|
1181
1398
|
this.activeNotifications.set(notificationId, notificationRef);
|
|
1182
1399
|
this.notificationOpenTimes.set(notificationId, Date.now());
|
|
1183
1400
|
}
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
detailsBtn.title = this.messages.showDetails;
|
|
1204
|
-
detailsBtn.onclick = (event) => {
|
|
1205
|
-
event.stopPropagation();
|
|
1206
|
-
this.messageDetailsDialogService.showDetailsDialog({
|
|
1207
|
-
title,
|
|
1208
|
-
details,
|
|
1209
|
-
level: 'warning'
|
|
1210
|
-
});
|
|
1211
|
-
};
|
|
1212
|
-
// Assemble the elements
|
|
1213
|
-
wrapper.appendChild(textSpan);
|
|
1214
|
-
wrapper.appendChild(detailsBtn);
|
|
1215
|
-
// Replace content
|
|
1216
|
-
contentEl.innerHTML = '';
|
|
1217
|
-
contentEl.appendChild(wrapper);
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
}, 50);
|
|
1401
|
+
const toast = { id: notificationId, key, style, title, ref: notificationRef, hideAfter: duration, hovered: false, focused: false };
|
|
1402
|
+
this.toasts.push(toast);
|
|
1403
|
+
notificationRef.afterHide?.subscribe(() => this.forget(notificationId));
|
|
1404
|
+
this.schedule(toast);
|
|
1405
|
+
this.enforceLimit();
|
|
1406
|
+
// The toast element exists once Kendo has rendered it.
|
|
1407
|
+
setTimeout(() => {
|
|
1408
|
+
this.watchPointerAndFocus(toast);
|
|
1409
|
+
if (details && sticky) {
|
|
1410
|
+
this.addDetailsButton(notificationId, title, details, style);
|
|
1411
|
+
}
|
|
1412
|
+
}, 50);
|
|
1413
|
+
}
|
|
1414
|
+
/** (Re)starts the auto-hide timer of a toast; a toast with `hideAfter` 0 stays, a hovered or focused one waits. */
|
|
1415
|
+
schedule(toast) {
|
|
1416
|
+
clearTimeout(toast.timer);
|
|
1417
|
+
toast.timer = undefined;
|
|
1418
|
+
if (toast.hideAfter > 0 && !toast.hovered && !toast.focused) {
|
|
1419
|
+
toast.timer = setTimeout(() => this.hideToast(toast), toast.hideAfter);
|
|
1221
1420
|
}
|
|
1222
1421
|
}
|
|
1223
|
-
/**
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
const
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
},
|
|
1235
|
-
animation: { type: 'slide', duration: 400 },
|
|
1236
|
-
hideAfter: 0, // Disable Kendo's auto-hide, we'll handle it manually
|
|
1237
|
-
closable: true,
|
|
1238
|
-
position: { horizontal: 'center', vertical: 'top' }
|
|
1422
|
+
/** Pauses the auto-hide while the pointer is over the toast or focus is inside it; restarts it afterwards. */
|
|
1423
|
+
watchPointerAndFocus(toast) {
|
|
1424
|
+
const element = document.querySelector(`.notification-${toast.id}`);
|
|
1425
|
+
if (!element || !this.toasts.includes(toast)) {
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
const update = (change) => {
|
|
1429
|
+
Object.assign(toast, change);
|
|
1430
|
+
if (this.toasts.includes(toast)) {
|
|
1431
|
+
this.schedule(toast);
|
|
1432
|
+
}
|
|
1239
1433
|
};
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1434
|
+
element.addEventListener('mouseenter', () => update({ hovered: true }));
|
|
1435
|
+
element.addEventListener('mouseleave', () => update({ hovered: false }));
|
|
1436
|
+
element.addEventListener('focusin', () => update({ focused: true }));
|
|
1437
|
+
element.addEventListener('focusout', (event) => {
|
|
1438
|
+
if (!(event.relatedTarget instanceof Node && element.contains(event.relatedTarget))) {
|
|
1439
|
+
update({ focused: false });
|
|
1440
|
+
}
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1443
|
+
/** Beyond `maxVisible`, hides the oldest toast of the lowest severity (success/info before warning before error). */
|
|
1444
|
+
enforceLimit() {
|
|
1445
|
+
const max = this.options.maxVisible;
|
|
1446
|
+
while (max > 0 && this.toasts.length > max) {
|
|
1447
|
+
const lowest = Math.min(...this.toasts.map(toast => SEVERITY[toast.style]));
|
|
1448
|
+
const victim = this.toasts.find(toast => SEVERITY[toast.style] === lowest);
|
|
1449
|
+
if (!victim) {
|
|
1450
|
+
return;
|
|
1451
|
+
}
|
|
1452
|
+
this.hideToast(victim);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
hideToast(toast) {
|
|
1456
|
+
toast.ref.hide();
|
|
1457
|
+
this.activeNotifications.delete(toast.id);
|
|
1458
|
+
this.notificationOpenTimes.delete(toast.id);
|
|
1459
|
+
this.forget(toast.id);
|
|
1460
|
+
}
|
|
1461
|
+
forget(id) {
|
|
1462
|
+
const toast = this.toasts.find(entry => entry.id === id);
|
|
1463
|
+
if (toast) {
|
|
1464
|
+
clearTimeout(toast.timer);
|
|
1465
|
+
this.toasts = this.toasts.filter(entry => entry !== toast);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
addDetailsButton(notificationId, title, details, level) {
|
|
1469
|
+
// Called once the notification is rendered: find it by the unique class and modify its content
|
|
1470
|
+
{
|
|
1471
|
+
const notification = document.querySelector(`.notification-${notificationId}`);
|
|
1472
|
+
if (notification) {
|
|
1473
|
+
const contentEl = notification.querySelector('.k-notification-content');
|
|
1474
|
+
if (contentEl && !contentEl.querySelector('.notification-details-btn')) {
|
|
1475
|
+
// Create wrapper div
|
|
1476
|
+
const wrapper = document.createElement('div');
|
|
1477
|
+
wrapper.style.cssText = 'display: flex; align-items: center; justify-content: space-between; width: 100%; gap: 10px;';
|
|
1478
|
+
// Create text span
|
|
1479
|
+
const textSpan = document.createElement('span');
|
|
1480
|
+
textSpan.style.cssText = 'flex: 1; overflow: hidden; text-overflow: ellipsis;';
|
|
1481
|
+
textSpan.textContent = title;
|
|
1482
|
+
// Create details button with icon only
|
|
1483
|
+
const detailsBtn = document.createElement('button');
|
|
1484
|
+
detailsBtn.className = 'k-button k-button-sm k-button-flat k-button-flat-base notification-details-btn';
|
|
1485
|
+
detailsBtn.style.cssText = 'flex-shrink: 0; width: 32px; height: 32px; padding: 8px; display: flex; align-items: center; justify-content: center;';
|
|
1486
|
+
detailsBtn.innerHTML = this.createSvgIcon();
|
|
1487
|
+
detailsBtn.title = this.messages.showDetails;
|
|
1488
|
+
detailsBtn.onclick = (event) => {
|
|
1489
|
+
event.stopPropagation();
|
|
1490
|
+
this.messageDetailsDialogService.showDetailsDialog({
|
|
1491
|
+
title,
|
|
1492
|
+
details,
|
|
1493
|
+
level
|
|
1494
|
+
});
|
|
1495
|
+
};
|
|
1496
|
+
// Assemble the elements
|
|
1497
|
+
wrapper.appendChild(textSpan);
|
|
1498
|
+
wrapper.appendChild(detailsBtn);
|
|
1499
|
+
// Replace content
|
|
1500
|
+
contentEl.innerHTML = '';
|
|
1501
|
+
contentEl.appendChild(wrapper);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1250
1504
|
}
|
|
1251
1505
|
}
|
|
1252
1506
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NotificationDisplayService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
@@ -1347,7 +1601,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
1347
1601
|
}] });
|
|
1348
1602
|
|
|
1349
1603
|
/**
|
|
1350
|
-
* Default English messages for the ListViewComponent.
|
|
1604
|
+
* Default English messages for the ListViewComponent. Complete: every member, including the
|
|
1605
|
+
* optional ones, has a value.
|
|
1351
1606
|
*/
|
|
1352
1607
|
const DEFAULT_LIST_VIEW_MESSAGES = {
|
|
1353
1608
|
searchPlaceholder: 'Search in all columns...',
|
|
@@ -1358,6 +1613,10 @@ const DEFAULT_LIST_VIEW_MESSAGES = {
|
|
|
1358
1613
|
resetFilters: 'Reset Filters',
|
|
1359
1614
|
commands: 'Commands',
|
|
1360
1615
|
actionsColumnTitle: 'Actions',
|
|
1616
|
+
rowActionsFor: 'Actions for {name}',
|
|
1617
|
+
rowActions: 'Row actions',
|
|
1618
|
+
rowActionUnavailable: 'Not available for this row',
|
|
1619
|
+
rowActionsLoading: 'The list is loading',
|
|
1361
1620
|
pdfPageTemplate: 'Page {pageNum} of {totalPages}',
|
|
1362
1621
|
pagerItemsPerPage: 'items per page',
|
|
1363
1622
|
pagerOf: 'of',
|
|
@@ -1368,7 +1627,30 @@ const DEFAULT_LIST_VIEW_MESSAGES = {
|
|
|
1368
1627
|
pagerPreviousPage: 'Go to the previous page',
|
|
1369
1628
|
pagerNextPage: 'Go to the next page',
|
|
1370
1629
|
noRecords: 'No records available.',
|
|
1630
|
+
noResultsTitle: 'No results',
|
|
1631
|
+
noResultsText: 'Nothing matches the current search or filter.',
|
|
1632
|
+
booleanYes: 'Yes',
|
|
1633
|
+
booleanNo: 'No',
|
|
1634
|
+
booleanIconLabel: '{column}: {value}',
|
|
1371
1635
|
};
|
|
1636
|
+
/**
|
|
1637
|
+
* Merges app-supplied list-view messages over {@link DEFAULT_LIST_VIEW_MESSAGES}. Members that
|
|
1638
|
+
* are missing, `undefined` or `null` keep their default, so an app compiled against an older
|
|
1639
|
+
* `ListViewMessages` shape (or one that leaves a translation unset) never renders an empty label.
|
|
1640
|
+
*/
|
|
1641
|
+
function resolveListViewMessages(messages) {
|
|
1642
|
+
const resolved = { ...DEFAULT_LIST_VIEW_MESSAGES };
|
|
1643
|
+
if (!messages) {
|
|
1644
|
+
return resolved;
|
|
1645
|
+
}
|
|
1646
|
+
for (const key of Object.keys(messages)) {
|
|
1647
|
+
const value = messages[key];
|
|
1648
|
+
if (value !== undefined && value !== null) {
|
|
1649
|
+
resolved[key] = value;
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
return resolved;
|
|
1653
|
+
}
|
|
1372
1654
|
|
|
1373
1655
|
class PascalCasePipe {
|
|
1374
1656
|
transform(value) {
|
|
@@ -1548,6 +1830,14 @@ class MmListViewDataBindingDirective extends DataBindingDirective {
|
|
|
1548
1830
|
get isLoading() {
|
|
1549
1831
|
return this.dataSource?.isLoading ?? false;
|
|
1550
1832
|
}
|
|
1833
|
+
/**
|
|
1834
|
+
* Read-only view of the grid state (filter, sort, skip, take) Kendo keeps
|
|
1835
|
+
* protected on the base directive. The list view consults it to decide
|
|
1836
|
+
* whether anything is filtered right now.
|
|
1837
|
+
*/
|
|
1838
|
+
get currentState() {
|
|
1839
|
+
return this.state;
|
|
1840
|
+
}
|
|
1551
1841
|
_serviceSubscription;
|
|
1552
1842
|
_executeFilterSubscription;
|
|
1553
1843
|
_fetchAgainSubscription;
|
|
@@ -1762,6 +2052,313 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
1762
2052
|
}]
|
|
1763
2053
|
}], ctorParameters: () => [] });
|
|
1764
2054
|
|
|
2055
|
+
/** Default number of row action slots (inline buttons incl. the overflow button). */
|
|
2056
|
+
const MM_ROW_ACTIONS_MAX_INLINE = 3;
|
|
2057
|
+
/**
|
|
2058
|
+
* Canonical action icons (AB#5570). One verb, one icon, everywhere: pick from this map
|
|
2059
|
+
* instead of importing a Kendo icon ad hoc, so "Edit" never shows a gear in one list and a
|
|
2060
|
+
* pencil in the next.
|
|
2061
|
+
*/
|
|
2062
|
+
const MM_ACTION_ICONS = {
|
|
2063
|
+
/** Create a new item ("New adapter", "Add mapping"). */
|
|
2064
|
+
add: plusIcon,
|
|
2065
|
+
/** Edit / open the editable form of the item. */
|
|
2066
|
+
edit: pencilIcon,
|
|
2067
|
+
/** Read-only view / preview. */
|
|
2068
|
+
view: eyeIcon,
|
|
2069
|
+
/** Open the item (or a related page) in its own view / new tab. */
|
|
2070
|
+
open: hyperlinkOpenIcon,
|
|
2071
|
+
/** Delete / remove the item (always `danger`). */
|
|
2072
|
+
delete: trashIcon,
|
|
2073
|
+
/** Clear a stored value without deleting the item ("Not needed", "Clear secret"). */
|
|
2074
|
+
clear: eraserIcon,
|
|
2075
|
+
/** Copy an id or value to the clipboard. */
|
|
2076
|
+
copy: copyIcon,
|
|
2077
|
+
/** Duplicate the item as a new item. */
|
|
2078
|
+
duplicate: fileDataIcon,
|
|
2079
|
+
/** Reload data. */
|
|
2080
|
+
refresh: arrowRotateCwIcon,
|
|
2081
|
+
/** Deploy to a pool / cluster. */
|
|
2082
|
+
deploy: uploadIcon,
|
|
2083
|
+
/** Undeploy. */
|
|
2084
|
+
undeploy: xCircleIcon,
|
|
2085
|
+
/** Run / execute / start. */
|
|
2086
|
+
run: playIcon,
|
|
2087
|
+
/** Stop / cancel a running job. */
|
|
2088
|
+
stop: stopIcon,
|
|
2089
|
+
/** Export data (file the user saves). */
|
|
2090
|
+
export: exportIcon,
|
|
2091
|
+
/** Import data. */
|
|
2092
|
+
import: importIcon,
|
|
2093
|
+
/** Download a file. */
|
|
2094
|
+
download: downloadIcon,
|
|
2095
|
+
/** Show a report / log. */
|
|
2096
|
+
report: fileReportIcon,
|
|
2097
|
+
/** Restore / roll back to a stored state (e.g. restore a dump); destructive restores are `danger`. */
|
|
2098
|
+
restore: undoIcon,
|
|
2099
|
+
/** Pin to a start page / favourites ("Pin to Home"). */
|
|
2100
|
+
pin: pinIcon,
|
|
2101
|
+
/** Remove a pin ("Unpin from Home"). */
|
|
2102
|
+
unpin: unpinIcon,
|
|
2103
|
+
/** Merge one item into another (e.g. merge users); usually `danger`. */
|
|
2104
|
+
merge: cellsMergeIcon,
|
|
2105
|
+
/** Reset / set a password. */
|
|
2106
|
+
resetPassword: passwordIcon,
|
|
2107
|
+
/** Fill a past time range from the source data (e.g. rollup backfill). */
|
|
2108
|
+
backfill: clockArrowRotateIcon,
|
|
2109
|
+
/** Recalculate derived values for a range (e.g. rollup recompute); not the same as `refresh`. */
|
|
2110
|
+
recompute: calculatorIcon,
|
|
2111
|
+
};
|
|
2112
|
+
/** True when the action is disabled (non-empty {@link MmAction.disabledReason}). */
|
|
2113
|
+
function isActionDisabled(action) {
|
|
2114
|
+
return !!action.disabledReason && action.disabledReason.trim().length > 0;
|
|
2115
|
+
}
|
|
2116
|
+
/** True unless the action is explicitly hidden. */
|
|
2117
|
+
function isActionVisible(action) {
|
|
2118
|
+
return action.visible !== false;
|
|
2119
|
+
}
|
|
2120
|
+
/**
|
|
2121
|
+
* Accessible name of an action on a named target: "Delete dump Encrypt run 2026-10-06 17:09".
|
|
2122
|
+
* Without a target the label alone.
|
|
2123
|
+
*/
|
|
2124
|
+
function actionAccessibleName(action, targetLabel) {
|
|
2125
|
+
const target = (targetLabel ?? '').trim();
|
|
2126
|
+
return target ? `${action.label} ${target}` : action.label;
|
|
2127
|
+
}
|
|
2128
|
+
/** Tooltip: the label, plus the reason when disabled ("Delete dump — Requires SecretManagement"). */
|
|
2129
|
+
function actionTooltip(action) {
|
|
2130
|
+
return isActionDisabled(action) ? `${action.label} — ${action.disabledReason.trim()}` : action.label;
|
|
2131
|
+
}
|
|
2132
|
+
/**
|
|
2133
|
+
* Splits visible actions into inline buttons and overflow-menu items. At most `maxInline`
|
|
2134
|
+
* slots are rendered: when the actions do not fit, the last slot becomes the overflow
|
|
2135
|
+
* button. Actions with `overflow: true` always go to the menu (and need the overflow slot).
|
|
2136
|
+
*/
|
|
2137
|
+
function splitRowActions(actions, maxInline = MM_ROW_ACTIONS_MAX_INLINE) {
|
|
2138
|
+
const visible = actions.filter(isActionVisible);
|
|
2139
|
+
const forced = visible.filter((a) => a.overflow);
|
|
2140
|
+
const candidates = visible.filter((a) => !a.overflow);
|
|
2141
|
+
const slots = Math.max(1, Math.floor(maxInline));
|
|
2142
|
+
const needsMenu = forced.length > 0 || candidates.length > slots;
|
|
2143
|
+
if (!needsMenu) {
|
|
2144
|
+
return { inline: candidates, menu: [] };
|
|
2145
|
+
}
|
|
2146
|
+
const inlineCount = Math.max(0, slots - 1);
|
|
2147
|
+
return {
|
|
2148
|
+
inline: candidates.slice(0, inlineCount),
|
|
2149
|
+
menu: [...candidates.slice(inlineCount), ...forced],
|
|
2150
|
+
};
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
let nextReasonId = 0;
|
|
2154
|
+
/**
|
|
2155
|
+
* One action as a button (AB#5570), rendered from a shared {@link MmAction} definition.
|
|
2156
|
+
*
|
|
2157
|
+
* - `display="icon"`: icon-only, native tooltip = label (+ disabled reason), `aria-label`
|
|
2158
|
+
* = label + `targetLabel` ("Delete dump Encrypt run 2026-10-06 17:09").
|
|
2159
|
+
* - `display="text"` / `"icon-text"`: visible label; `targetLabel` still extends the
|
|
2160
|
+
* accessible name when the same label repeats (one per row).
|
|
2161
|
+
* - Disabled (`disabledReason`): stays focusable, `aria-disabled="true"`, the reason is
|
|
2162
|
+
* linked via `aria-describedby` and shown in the tooltip; clicks are swallowed.
|
|
2163
|
+
* - `danger`: Kendo `themeColor="error"`. The consumer confirms before acting.
|
|
2164
|
+
* - `primary` (page/toolbar context only): solid primary button for the one main action.
|
|
2165
|
+
* - `link`: rendered as a real router link (`<a kendoButton [routerLink]>`, so open-in-new-tab
|
|
2166
|
+
* works); a disabled link action renders as a disabled button.
|
|
2167
|
+
*
|
|
2168
|
+
* ```html
|
|
2169
|
+
* <mm-action-button [action]="refresh" context="toolbar" (triggered)="reload()" />
|
|
2170
|
+
* <mm-action-button [action]="newAdapter" context="page" display="icon-text" primary (triggered)="create()" />
|
|
2171
|
+
* ```
|
|
2172
|
+
*/
|
|
2173
|
+
class ActionButtonComponent {
|
|
2174
|
+
/** The action definition. */
|
|
2175
|
+
action = input.required(/* @ts-ignore */
|
|
2176
|
+
...(ngDevMode ? [{ debugName: "action" }] : /* istanbul ignore next */ []));
|
|
2177
|
+
/** Name of the target (row) for the accessible name; required for repeated row actions. */
|
|
2178
|
+
targetLabel = input(undefined, /* @ts-ignore */
|
|
2179
|
+
...(ngDevMode ? [{ debugName: "targetLabel" }] : /* istanbul ignore next */ []));
|
|
2180
|
+
context = input('row', /* @ts-ignore */
|
|
2181
|
+
...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
|
|
2182
|
+
display = input('icon', /* @ts-ignore */
|
|
2183
|
+
...(ngDevMode ? [{ debugName: "display" }] : /* istanbul ignore next */ []));
|
|
2184
|
+
/** Solid primary emphasis — at most one per page header / toolbar. */
|
|
2185
|
+
primary = input(false, { ...(ngDevMode ? { debugName: "primary" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
2186
|
+
/** Fires for enabled actions only. */
|
|
2187
|
+
triggered = output();
|
|
2188
|
+
reasonId = `mm-action-reason-${nextReasonId++}`;
|
|
2189
|
+
disabled = computed(() => isActionDisabled(this.action()), /* @ts-ignore */
|
|
2190
|
+
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
2191
|
+
/** Icon-only needs an icon; without one the button falls back to its text. */
|
|
2192
|
+
iconOnly = computed(() => this.display() === 'icon' && !!this.action().icon, /* @ts-ignore */
|
|
2193
|
+
...(ngDevMode ? [{ debugName: "iconOnly" }] : /* istanbul ignore next */ []));
|
|
2194
|
+
/** Visible text: `menuLabel` ("Delete dump…") when set, else the label. */
|
|
2195
|
+
visibleLabel = computed(() => this.action().menuLabel || this.action().label, /* @ts-ignore */
|
|
2196
|
+
...(ngDevMode ? [{ debugName: "visibleLabel" }] : /* istanbul ignore next */ []));
|
|
2197
|
+
showIcon = computed(() => this.display() !== 'text' && !!this.action().icon, /* @ts-ignore */
|
|
2198
|
+
...(ngDevMode ? [{ debugName: "showIcon" }] : /* istanbul ignore next */ []));
|
|
2199
|
+
tooltip = computed(() => this.iconOnly() || this.disabled() ? actionTooltip(this.action()) : null, /* @ts-ignore */
|
|
2200
|
+
...(ngDevMode ? [{ debugName: "tooltip" }] : /* istanbul ignore next */ []));
|
|
2201
|
+
ariaLabel = computed(() => this.iconOnly() || this.targetLabel() ? actionAccessibleName(this.action(), this.targetLabel()) : null, /* @ts-ignore */
|
|
2202
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
2203
|
+
/** Kendo button classes for the link variant (same look as `button[kendoButton]`). */
|
|
2204
|
+
linkClasses = computed(() => {
|
|
2205
|
+
const size = this.context() === 'row' ? 'sm' : 'md';
|
|
2206
|
+
const fill = this.primary() && !this.action().danger ? 'solid' : 'flat';
|
|
2207
|
+
const theme = this.action().danger ? 'error' : this.primary() ? 'primary' : 'base';
|
|
2208
|
+
return ['k-button', `k-button-${size}`, `k-button-${fill}`, `k-button-${theme}`, 'k-rounded-md',
|
|
2209
|
+
this.iconOnly() ? 'k-icon-button' : ''].filter(Boolean).join(' ');
|
|
2210
|
+
}, /* @ts-ignore */
|
|
2211
|
+
...(ngDevMode ? [{ debugName: "linkClasses" }] : /* istanbul ignore next */ []));
|
|
2212
|
+
onClick(event) {
|
|
2213
|
+
if (this.disabled()) {
|
|
2214
|
+
event.preventDefault();
|
|
2215
|
+
event.stopPropagation();
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2218
|
+
const action = this.action();
|
|
2219
|
+
this.triggered.emit({ id: action.id, action });
|
|
2220
|
+
}
|
|
2221
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ActionButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2222
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: ActionButtonComponent, isStandalone: true, selector: "mm-action-button", inputs: { action: { classPropertyName: "action", publicName: "action", isSignal: true, isRequired: true, transformFunction: null }, targetLabel: { classPropertyName: "targetLabel", publicName: "targetLabel", isSignal: true, isRequired: false, transformFunction: null }, context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: false, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null }, primary: { classPropertyName: "primary", publicName: "primary", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { triggered: "triggered" }, host: { properties: { "class.mm-action-button-host--disabled": "disabled()" }, classAttribute: "mm-action-button-host" }, ngImport: i0, template: `
|
|
2223
|
+
@if (action().link && !disabled()) {
|
|
2224
|
+
<!-- kendoButton only matches <button>: the link carries the same Kendo button classes itself. -->
|
|
2225
|
+
<a
|
|
2226
|
+
class="mm-action-button"
|
|
2227
|
+
[class]="linkClasses()"
|
|
2228
|
+
[class.mm-action-button--danger]="!!action().danger"
|
|
2229
|
+
[class.mm-action-button--icon-only]="iconOnly()"
|
|
2230
|
+
[class.mm-action-button--toolbar]="context() !== 'row'"
|
|
2231
|
+
[attr.title]="tooltip()"
|
|
2232
|
+
[attr.aria-label]="ariaLabel()"
|
|
2233
|
+
[attr.data-action]="action().id"
|
|
2234
|
+
[routerLink]="$any(action().link!.commands)"
|
|
2235
|
+
[queryParams]="action().link!.queryParams ?? null"
|
|
2236
|
+
(click)="onClick($event)"
|
|
2237
|
+
>@if (showIcon()) {<kendo-svgicon class="k-button-icon" [icon]="action().icon!" />}@if (!iconOnly()) {<span class="k-button-text">{{ visibleLabel() }}</span>}</a>
|
|
2238
|
+
} @else {
|
|
2239
|
+
<button
|
|
2240
|
+
kendoButton
|
|
2241
|
+
type="button"
|
|
2242
|
+
class="mm-action-button"
|
|
2243
|
+
[class.mm-action-button--disabled]="disabled()"
|
|
2244
|
+
[class.mm-action-button--danger]="!!action().danger"
|
|
2245
|
+
[class.mm-action-button--icon-only]="iconOnly()"
|
|
2246
|
+
[class.mm-action-button--toolbar]="context() !== 'row'"
|
|
2247
|
+
[svgIcon]="showIcon() ? action().icon! : $any(undefined)"
|
|
2248
|
+
[size]="context() === 'row' ? 'small' : 'medium'"
|
|
2249
|
+
[fillMode]="primary() && !action().danger ? 'solid' : 'flat'"
|
|
2250
|
+
[themeColor]="action().danger ? 'error' : primary() ? 'primary' : 'base'"
|
|
2251
|
+
[attr.title]="tooltip()"
|
|
2252
|
+
[attr.aria-label]="ariaLabel()"
|
|
2253
|
+
[attr.data-action]="action().id"
|
|
2254
|
+
[attr.aria-disabled]="disabled() ? 'true' : null"
|
|
2255
|
+
[attr.aria-describedby]="disabled() ? reasonId : null"
|
|
2256
|
+
(click)="onClick($event)"
|
|
2257
|
+
>@if (!iconOnly()) { {{ visibleLabel() }} }</button>
|
|
2258
|
+
}
|
|
2259
|
+
@if (disabled()) {
|
|
2260
|
+
<span class="mm-action-button__reason" [id]="reasonId">{{ action().disabledReason }}</span>
|
|
2261
|
+
}
|
|
2262
|
+
`, isInline: true, styles: [".mm-action-button-host{display:inline-flex;position:relative}.mm-action-button--toolbar.mm-action-button--icon-only .k-svg-icon{width:20px;height:20px}.mm-action-button--disabled{opacity:.45;cursor:not-allowed}.mm-action-button-host .mm-action-button:focus-visible{outline:none;box-shadow:var(--theme-focus-ring, 0 0 0 2px #ffffff, 0 0 0 4px #2e8473)}.mm-action-button__reason{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.mm-action-button-host--disabled:has(.mm-action-button:focus-visible) .mm-action-button__reason{top:calc(100% + 4px);left:0;z-index:20;width:max-content;max-width:280px;height:auto;margin:0;overflow:visible;clip:auto;padding:var(--theme-space-1, 4px) var(--theme-space-2, 8px);white-space:normal;font-size:.75rem;line-height:1.4;color:var(--theme-text-primary, #0f1c2e);background:var(--theme-bg-overlay, #ffffff);border:1px solid var(--theme-border-default, rgba(7, 23, 43, .12));border-radius:var(--theme-radius-sm, 4px);box-shadow:var(--theme-shadow-popup, 0 8px 24px rgba(7, 23, 43, .14));pointer-events:none}\n"], dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i2.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon", "variant"], exportAs: ["kendoSVGIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
2263
|
+
}
|
|
2264
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ActionButtonComponent, decorators: [{
|
|
2265
|
+
type: Component,
|
|
2266
|
+
args: [{ selector: 'mm-action-button', standalone: true, imports: [ButtonModule, RouterLink, SVGIconModule], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { class: 'mm-action-button-host', '[class.mm-action-button-host--disabled]': 'disabled()' }, template: `
|
|
2267
|
+
@if (action().link && !disabled()) {
|
|
2268
|
+
<!-- kendoButton only matches <button>: the link carries the same Kendo button classes itself. -->
|
|
2269
|
+
<a
|
|
2270
|
+
class="mm-action-button"
|
|
2271
|
+
[class]="linkClasses()"
|
|
2272
|
+
[class.mm-action-button--danger]="!!action().danger"
|
|
2273
|
+
[class.mm-action-button--icon-only]="iconOnly()"
|
|
2274
|
+
[class.mm-action-button--toolbar]="context() !== 'row'"
|
|
2275
|
+
[attr.title]="tooltip()"
|
|
2276
|
+
[attr.aria-label]="ariaLabel()"
|
|
2277
|
+
[attr.data-action]="action().id"
|
|
2278
|
+
[routerLink]="$any(action().link!.commands)"
|
|
2279
|
+
[queryParams]="action().link!.queryParams ?? null"
|
|
2280
|
+
(click)="onClick($event)"
|
|
2281
|
+
>@if (showIcon()) {<kendo-svgicon class="k-button-icon" [icon]="action().icon!" />}@if (!iconOnly()) {<span class="k-button-text">{{ visibleLabel() }}</span>}</a>
|
|
2282
|
+
} @else {
|
|
2283
|
+
<button
|
|
2284
|
+
kendoButton
|
|
2285
|
+
type="button"
|
|
2286
|
+
class="mm-action-button"
|
|
2287
|
+
[class.mm-action-button--disabled]="disabled()"
|
|
2288
|
+
[class.mm-action-button--danger]="!!action().danger"
|
|
2289
|
+
[class.mm-action-button--icon-only]="iconOnly()"
|
|
2290
|
+
[class.mm-action-button--toolbar]="context() !== 'row'"
|
|
2291
|
+
[svgIcon]="showIcon() ? action().icon! : $any(undefined)"
|
|
2292
|
+
[size]="context() === 'row' ? 'small' : 'medium'"
|
|
2293
|
+
[fillMode]="primary() && !action().danger ? 'solid' : 'flat'"
|
|
2294
|
+
[themeColor]="action().danger ? 'error' : primary() ? 'primary' : 'base'"
|
|
2295
|
+
[attr.title]="tooltip()"
|
|
2296
|
+
[attr.aria-label]="ariaLabel()"
|
|
2297
|
+
[attr.data-action]="action().id"
|
|
2298
|
+
[attr.aria-disabled]="disabled() ? 'true' : null"
|
|
2299
|
+
[attr.aria-describedby]="disabled() ? reasonId : null"
|
|
2300
|
+
(click)="onClick($event)"
|
|
2301
|
+
>@if (!iconOnly()) { {{ visibleLabel() }} }</button>
|
|
2302
|
+
}
|
|
2303
|
+
@if (disabled()) {
|
|
2304
|
+
<span class="mm-action-button__reason" [id]="reasonId">{{ action().disabledReason }}</span>
|
|
2305
|
+
}
|
|
2306
|
+
`, styles: [".mm-action-button-host{display:inline-flex;position:relative}.mm-action-button--toolbar.mm-action-button--icon-only .k-svg-icon{width:20px;height:20px}.mm-action-button--disabled{opacity:.45;cursor:not-allowed}.mm-action-button-host .mm-action-button:focus-visible{outline:none;box-shadow:var(--theme-focus-ring, 0 0 0 2px #ffffff, 0 0 0 4px #2e8473)}.mm-action-button__reason{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.mm-action-button-host--disabled:has(.mm-action-button:focus-visible) .mm-action-button__reason{top:calc(100% + 4px);left:0;z-index:20;width:max-content;max-width:280px;height:auto;margin:0;overflow:visible;clip:auto;padding:var(--theme-space-1, 4px) var(--theme-space-2, 8px);white-space:normal;font-size:.75rem;line-height:1.4;color:var(--theme-text-primary, #0f1c2e);background:var(--theme-bg-overlay, #ffffff);border:1px solid var(--theme-border-default, rgba(7, 23, 43, .12));border-radius:var(--theme-radius-sm, 4px);box-shadow:var(--theme-shadow-popup, 0 8px 24px rgba(7, 23, 43, .14));pointer-events:none}\n"] }]
|
|
2307
|
+
}], propDecorators: { action: [{ type: i0.Input, args: [{ isSignal: true, alias: "action", required: true }] }], targetLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "targetLabel", required: false }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: false }] }], display: [{ type: i0.Input, args: [{ isSignal: true, alias: "display", required: false }] }], primary: [{ type: i0.Input, args: [{ isSignal: true, alias: "primary", required: false }] }], triggered: [{ type: i0.Output, args: ["triggered"] }] } });
|
|
2308
|
+
|
|
2309
|
+
/**
|
|
2310
|
+
* Human label of a list row (AB#5623) — the row's name in row action names ("Actions for <label>"),
|
|
2311
|
+
* dialog titles and danger confirmations. Reads `field` (a dotted path such as `contact.displayName`
|
|
2312
|
+
* also works, a flat key with dots wins), then each of `fallbacks` (default `rtWellKnownName`,
|
|
2313
|
+
* `rtId`); the first non-blank value wins. `''` when none is set.
|
|
2314
|
+
*/
|
|
2315
|
+
function resolveListRowLabel(row, field, fallbacks = ['rtWellKnownName', 'rtId']) {
|
|
2316
|
+
const record = (row ?? {});
|
|
2317
|
+
for (const key of field ? [field, ...fallbacks] : fallbacks) {
|
|
2318
|
+
const value = readRowPath(record, key);
|
|
2319
|
+
if (value !== null && value !== undefined && typeof value !== 'object' && typeof value !== 'function' && String(value).trim() !== '') {
|
|
2320
|
+
return String(value);
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
return '';
|
|
2324
|
+
}
|
|
2325
|
+
function readRowPath(row, path) {
|
|
2326
|
+
if (path in row || path.indexOf('.') === -1) {
|
|
2327
|
+
return row[path];
|
|
2328
|
+
}
|
|
2329
|
+
let value = row;
|
|
2330
|
+
for (const key of path.split('.')) {
|
|
2331
|
+
if (value === null || value === undefined || typeof value !== 'object') {
|
|
2332
|
+
return undefined;
|
|
2333
|
+
}
|
|
2334
|
+
value = value[key];
|
|
2335
|
+
}
|
|
2336
|
+
return value;
|
|
2337
|
+
}
|
|
2338
|
+
/** Resolves the per-row callbacks of a {@link MmListRowAction}. */
|
|
2339
|
+
function resolveListRowAction(definition, row) {
|
|
2340
|
+
const { disabledReason, visible, link, run: _run, ...rest } = definition;
|
|
2341
|
+
return {
|
|
2342
|
+
...rest,
|
|
2343
|
+
disabledReason: typeof disabledReason === 'function' ? disabledReason(row) ?? null : disabledReason ?? null,
|
|
2344
|
+
visible: typeof visible === 'function' ? visible(row) : visible,
|
|
2345
|
+
link: typeof link === 'function' ? link(row) ?? undefined : link,
|
|
2346
|
+
};
|
|
2347
|
+
}
|
|
2348
|
+
/** Structural equality of two rendered actions (keeps object identity stable across change detection). */
|
|
2349
|
+
function sameAction(a, b) {
|
|
2350
|
+
return a.id === b.id && a.label === b.label && a.menuLabel === b.menuLabel && a.icon === b.icon
|
|
2351
|
+
&& !!a.danger === !!b.danger && (a.disabledReason ?? null) === (b.disabledReason ?? null)
|
|
2352
|
+
&& a.visible === b.visible && !!a.overflow === !!b.overflow && sameLink(a.link, b.link);
|
|
2353
|
+
}
|
|
2354
|
+
function sameLink(a, b) {
|
|
2355
|
+
if (a === b)
|
|
2356
|
+
return true;
|
|
2357
|
+
if (!a || !b)
|
|
2358
|
+
return false;
|
|
2359
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
2360
|
+
}
|
|
2361
|
+
|
|
1765
2362
|
class BytesToSizePipe {
|
|
1766
2363
|
transform(value, decimals = 2) {
|
|
1767
2364
|
if (value === 0) {
|
|
@@ -1901,6 +2498,248 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
1901
2498
|
}]
|
|
1902
2499
|
}] });
|
|
1903
2500
|
|
|
2501
|
+
/** Pixel edge length of each {@link OctoBotSize}. */
|
|
2502
|
+
const OCTOBOT_SIZE_PX = { sm: 60, md: 120, lg: 240 };
|
|
2503
|
+
/**
|
|
2504
|
+
* Length of one loop of each animation in ms (identical for all sizes; read from the WebP frame
|
|
2505
|
+
* durations). Used by `playOnce`.
|
|
2506
|
+
*/
|
|
2507
|
+
const OCTOBOT_LOOP_MS = {
|
|
2508
|
+
idle: 1900, blink: 2140, look: 2320, swim: 1750, thinking: 3520, wave: 2210
|
|
2509
|
+
};
|
|
2510
|
+
/**
|
|
2511
|
+
* Base URL of the OctoBot images, with a trailing slash. Default `assets/octobot/` (relative to
|
|
2512
|
+
* the document base). The files ship as library assets in
|
|
2513
|
+
* `node_modules/@meshmakers/shared-ui/assets/octobot`; a host app copies them with an
|
|
2514
|
+
* `angular.json` assets entry:
|
|
2515
|
+
*
|
|
2516
|
+
* ```json
|
|
2517
|
+
* { "glob": "**\/*", "input": "node_modules/@meshmakers/shared-ui/assets/octobot", "output": "/assets/octobot" }
|
|
2518
|
+
* ```
|
|
2519
|
+
*
|
|
2520
|
+
* Provide a different value when the app serves them elsewhere (a CDN, a sub path).
|
|
2521
|
+
*/
|
|
2522
|
+
const OCTOBOT_ASSET_BASE_URL = new InjectionToken('OCTOBOT_ASSET_BASE_URL', {
|
|
2523
|
+
providedIn: 'root',
|
|
2524
|
+
factory: () => 'assets/octobot/'
|
|
2525
|
+
});
|
|
2526
|
+
/**
|
|
2527
|
+
* App-wide "show stills only" switch, e.g. bound to a user setting "Reduce animations". When
|
|
2528
|
+
* `true`, every `mm-octobot` shows its still frame regardless of `prefers-reduced-motion`.
|
|
2529
|
+
*/
|
|
2530
|
+
const OCTOBOT_FORCE_STILL = new InjectionToken('OCTOBOT_FORCE_STILL', {
|
|
2531
|
+
providedIn: 'root',
|
|
2532
|
+
factory: () => false
|
|
2533
|
+
});
|
|
2534
|
+
const REDUCED_MOTION_QUERY = '(prefers-reduced-motion: reduce)';
|
|
2535
|
+
/**
|
|
2536
|
+
* `mm-octobot` — the meshmakers OctoBot mascot as a small animated pixel-art image (AB#3444).
|
|
2537
|
+
*
|
|
2538
|
+
* Renders one `<img>` with a fixed width and height (no layout shift), `loading="lazy"` (or
|
|
2539
|
+
* `eager` via the `loading` input),
|
|
2540
|
+
* `decoding="async"` and `image-rendering: pixelated`. The image is an 8-frame animated WebP
|
|
2541
|
+
* loaded by URL from {@link OCTOBOT_ASSET_BASE_URL} — nothing lands in the JS bundle.
|
|
2542
|
+
*
|
|
2543
|
+
* **Reduced motion:** while the user prefers reduced motion (`prefers-reduced-motion: reduce`,
|
|
2544
|
+
* followed live via `matchMedia`), when {@link OCTOBOT_FORCE_STILL} is `true` or when the `still`
|
|
2545
|
+
* input is set, the first frame is shown as a static PNG instead.
|
|
2546
|
+
*
|
|
2547
|
+
* **Accessibility:** without a `label` the figure is decorative (`alt=""`, `aria-hidden="true"`);
|
|
2548
|
+
* the surrounding text must carry the meaning (e.g. "Thinking…" next to `thinking`). With a
|
|
2549
|
+
* `label` the host gets `role="img"` and `aria-label`.
|
|
2550
|
+
*
|
|
2551
|
+
* **Placement rules (host responsibility, see `docs/actions.md` › OctoBot):** at most one
|
|
2552
|
+
* animated OctoBot per view — empty states (once per page), page loading, the assistant panel,
|
|
2553
|
+
* 404 / turned-off pages, onboarding. Never inside repeated elements (list rows, table cells,
|
|
2554
|
+
* cockpit tiles, buttons) and never in toasts or error messages (a still frame is acceptable
|
|
2555
|
+
* there if a figure is wanted at all).
|
|
2556
|
+
*
|
|
2557
|
+
* @example
|
|
2558
|
+
* ```html
|
|
2559
|
+
* <mm-octobot animation="idle" size="md" />
|
|
2560
|
+
* <mm-octobot animation="thinking" size="sm" label="The assistant is thinking" />
|
|
2561
|
+
* ```
|
|
2562
|
+
*/
|
|
2563
|
+
class OctoBotComponent {
|
|
2564
|
+
baseUrl = inject(OCTOBOT_ASSET_BASE_URL);
|
|
2565
|
+
forceStill = inject(OCTOBOT_FORCE_STILL);
|
|
2566
|
+
/** Which animation to play. Default `idle`. */
|
|
2567
|
+
animation = input('idle', /* @ts-ignore */
|
|
2568
|
+
...(ngDevMode ? [{ debugName: "animation" }] : /* istanbul ignore next */ []));
|
|
2569
|
+
/** Rendered size. Default `md` (120 px). */
|
|
2570
|
+
size = input('md', /* @ts-ignore */
|
|
2571
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
2572
|
+
/** Accessible name; when empty the figure is decorative. */
|
|
2573
|
+
label = input(null, /* @ts-ignore */
|
|
2574
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
2575
|
+
/** Image loading strategy; `eager` where the figure is visible right away. Default `lazy`. */
|
|
2576
|
+
loading = input('lazy', /* @ts-ignore */
|
|
2577
|
+
...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
2578
|
+
/** Show the still frame instead of the animation (e.g. after the assistant finished). */
|
|
2579
|
+
still = input(false, { ...(ngDevMode ? { debugName: "still" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
2580
|
+
/**
|
|
2581
|
+
* Plays the animation once ({@link OCTOBOT_LOOP_MS}) and then rests on the still frame — for
|
|
2582
|
+
* greetings (`wave`) that should not loop forever. The WebPs themselves loop endlessly.
|
|
2583
|
+
*/
|
|
2584
|
+
playOnce = input(false, { ...(ngDevMode ? { debugName: "playOnce" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
2585
|
+
prefersReducedMotion = signal(false, /* @ts-ignore */
|
|
2586
|
+
...(ngDevMode ? [{ debugName: "prefersReducedMotion" }] : /* istanbul ignore next */ []));
|
|
2587
|
+
played = signal(false, /* @ts-ignore */
|
|
2588
|
+
...(ngDevMode ? [{ debugName: "played" }] : /* istanbul ignore next */ []));
|
|
2589
|
+
px = computed(() => OCTOBOT_SIZE_PX[this.size()] ?? OCTOBOT_SIZE_PX.md, /* @ts-ignore */
|
|
2590
|
+
...(ngDevMode ? [{ debugName: "px" }] : /* istanbul ignore next */ []));
|
|
2591
|
+
showStill = computed(() => this.still() || this.forceStill || this.prefersReducedMotion() || this.played(), /* @ts-ignore */
|
|
2592
|
+
...(ngDevMode ? [{ debugName: "showStill" }] : /* istanbul ignore next */ []));
|
|
2593
|
+
src = computed(() => {
|
|
2594
|
+
const base = this.baseUrl.endsWith('/') ? this.baseUrl : `${this.baseUrl}/`;
|
|
2595
|
+
const file = this.showStill() ? `octo_${this.animation()}_still.png` : `octo_${this.animation()}.webp`;
|
|
2596
|
+
return `${base}${this.size()}/${file}`;
|
|
2597
|
+
}, /* @ts-ignore */
|
|
2598
|
+
...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
|
|
2599
|
+
constructor() {
|
|
2600
|
+
const destroyRef = inject(DestroyRef);
|
|
2601
|
+
// Restart the single loop whenever the animation changes while playOnce is set.
|
|
2602
|
+
effect((onCleanup) => {
|
|
2603
|
+
const animation = this.animation();
|
|
2604
|
+
this.played.set(false);
|
|
2605
|
+
if (!this.playOnce()) {
|
|
2606
|
+
return;
|
|
2607
|
+
}
|
|
2608
|
+
const timer = setTimeout(() => this.played.set(true), OCTOBOT_LOOP_MS[animation] ?? 2000);
|
|
2609
|
+
onCleanup(() => clearTimeout(timer));
|
|
2610
|
+
});
|
|
2611
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID)) || typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
|
|
2612
|
+
return;
|
|
2613
|
+
}
|
|
2614
|
+
const query = window.matchMedia(REDUCED_MOTION_QUERY);
|
|
2615
|
+
if (!query) {
|
|
2616
|
+
// Stubbed or partial matchMedia (tests, embedded webviews): keep the animation, no live updates.
|
|
2617
|
+
return;
|
|
2618
|
+
}
|
|
2619
|
+
this.prefersReducedMotion.set(!!query.matches);
|
|
2620
|
+
const listener = (event) => this.prefersReducedMotion.set(event.matches);
|
|
2621
|
+
query.addEventListener?.('change', listener);
|
|
2622
|
+
destroyRef.onDestroy(() => query.removeEventListener?.('change', listener));
|
|
2623
|
+
}
|
|
2624
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: OctoBotComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2625
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.5", type: OctoBotComponent, isStandalone: true, selector: "mm-octobot", inputs: { animation: { classPropertyName: "animation", publicName: "animation", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, still: { classPropertyName: "still", publicName: "still", isSignal: true, isRequired: false, transformFunction: null }, playOnce: { classPropertyName: "playOnce", publicName: "playOnce", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-size": "size()", "attr.role": "label() ? \"img\" : null", "attr.aria-label": "label() || null", "attr.aria-hidden": "label() ? null : \"true\"", "attr.data-animation": "animation()", "attr.data-still": "showStill() ? \"true\" : null" }, classAttribute: "mm-octobot" }, ngImport: i0, template: `<img
|
|
2626
|
+
class="mm-octobot__img"
|
|
2627
|
+
[attr.loading]="loading()"
|
|
2628
|
+
[src]="src()"
|
|
2629
|
+
[attr.width]="px()"
|
|
2630
|
+
[attr.height]="px()"
|
|
2631
|
+
alt=""
|
|
2632
|
+
decoding="async"
|
|
2633
|
+
draggable="false" />`, isInline: true, styles: [":host{display:inline-block;flex:none;line-height:0}.mm-octobot__img{display:block;max-width:none;image-rendering:pixelated;-webkit-user-select:none;user-select:none}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2634
|
+
}
|
|
2635
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: OctoBotComponent, decorators: [{
|
|
2636
|
+
type: Component,
|
|
2637
|
+
args: [{ selector: 'mm-octobot', template: `<img
|
|
2638
|
+
class="mm-octobot__img"
|
|
2639
|
+
[attr.loading]="loading()"
|
|
2640
|
+
[src]="src()"
|
|
2641
|
+
[attr.width]="px()"
|
|
2642
|
+
[attr.height]="px()"
|
|
2643
|
+
alt=""
|
|
2644
|
+
decoding="async"
|
|
2645
|
+
draggable="false" />`, host: {
|
|
2646
|
+
'class': 'mm-octobot',
|
|
2647
|
+
'[attr.data-size]': 'size()',
|
|
2648
|
+
'[attr.role]': 'label() ? "img" : null',
|
|
2649
|
+
'[attr.aria-label]': 'label() || null',
|
|
2650
|
+
'[attr.aria-hidden]': 'label() ? null : "true"',
|
|
2651
|
+
'[attr.data-animation]': 'animation()',
|
|
2652
|
+
'[attr.data-still]': 'showStill() ? "true" : null'
|
|
2653
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:inline-block;flex:none;line-height:0}.mm-octobot__img{display:block;max-width:none;image-rendering:pixelated;-webkit-user-select:none;user-select:none}\n"] }]
|
|
2654
|
+
}], ctorParameters: () => [], propDecorators: { animation: [{ type: i0.Input, args: [{ isSignal: true, alias: "animation", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], still: [{ type: i0.Input, args: [{ isSignal: true, alias: "still", required: false }] }], playOnce: [{ type: i0.Input, args: [{ isSignal: true, alias: "playOnce", required: false }] }] } });
|
|
2655
|
+
|
|
2656
|
+
/** Default English "Try again" label of {@link EmptyStateComponent}. */
|
|
2657
|
+
const DEFAULT_EMPTY_STATE_RETRY_LABEL = 'Try again';
|
|
2658
|
+
/**
|
|
2659
|
+
* `mm-empty-state` — the shared empty / no-results / error block of a page or list (AB#3444).
|
|
2660
|
+
*
|
|
2661
|
+
* One per page or list, never per row. The `empty` and `no-results` variants show one OctoBot
|
|
2662
|
+
* (`idle` / `look`, decorative) above the title; the `error` variant shows no figure and announces
|
|
2663
|
+
* itself as an alert. Project extra actions (e.g. "New data flow") as content.
|
|
2664
|
+
*
|
|
2665
|
+
* @example
|
|
2666
|
+
* ```html
|
|
2667
|
+
* <mm-empty-state heading="No data flows yet" text="Create your first data flow to start processing data." />
|
|
2668
|
+
* <mm-empty-state variant="error" heading="The blueprints could not be loaded." showRetry (retry)="load()" />
|
|
2669
|
+
* ```
|
|
2670
|
+
*/
|
|
2671
|
+
class EmptyStateComponent {
|
|
2672
|
+
variant = input('empty', /* @ts-ignore */
|
|
2673
|
+
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
2674
|
+
heading = input.required(/* @ts-ignore */
|
|
2675
|
+
...(ngDevMode ? [{ debugName: "heading" }] : /* istanbul ignore next */ []));
|
|
2676
|
+
text = input(null, /* @ts-ignore */
|
|
2677
|
+
...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
|
|
2678
|
+
/** OctoBot size of the `empty` / `no-results` variants. Default `md`. */
|
|
2679
|
+
size = input('md', /* @ts-ignore */
|
|
2680
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
2681
|
+
/** Shows the "Try again" button (error variant only). */
|
|
2682
|
+
showRetry = input(false, { ...(ngDevMode ? { debugName: "showRetry" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
2683
|
+
retryLabel = input(DEFAULT_EMPTY_STATE_RETRY_LABEL, /* @ts-ignore */
|
|
2684
|
+
...(ngDevMode ? [{ debugName: "retryLabel" }] : /* istanbul ignore next */ []));
|
|
2685
|
+
/** Emitted by the "Try again" button. */
|
|
2686
|
+
retry = output();
|
|
2687
|
+
figure = computed(() => {
|
|
2688
|
+
switch (this.variant()) {
|
|
2689
|
+
case 'empty': return 'idle';
|
|
2690
|
+
case 'no-results': return 'look';
|
|
2691
|
+
default: return null;
|
|
2692
|
+
}
|
|
2693
|
+
}, /* @ts-ignore */
|
|
2694
|
+
...(ngDevMode ? [{ debugName: "figure" }] : /* istanbul ignore next */ []));
|
|
2695
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: EmptyStateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2696
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: EmptyStateComponent, isStandalone: true, selector: "mm-empty-state", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: true, transformFunction: null }, text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, showRetry: { classPropertyName: "showRetry", publicName: "showRetry", isSignal: true, isRequired: false, transformFunction: null }, retryLabel: { classPropertyName: "retryLabel", publicName: "retryLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { retry: "retry" }, host: { properties: { "attr.data-variant": "variant()" }, classAttribute: "mm-empty-state" }, ngImport: i0, template: `
|
|
2697
|
+
@if (figure(); as animation) {
|
|
2698
|
+
<mm-octobot class="mm-empty-state__figure" [animation]="animation" [size]="size()" loading="eager" />
|
|
2699
|
+
}
|
|
2700
|
+
<p class="mm-empty-state__title" [attr.role]="variant() === 'error' ? 'alert' : null">{{ heading() }}</p>
|
|
2701
|
+
@if (text()) {
|
|
2702
|
+
<p class="mm-empty-state__text">{{ text() }}</p>
|
|
2703
|
+
}
|
|
2704
|
+
@if (variant() === 'error' && showRetry()) {
|
|
2705
|
+
<button kendoButton type="button" class="mm-empty-state__retry" (click)="retry.emit()">{{ retryLabel() }}</button>
|
|
2706
|
+
}
|
|
2707
|
+
<ng-content />
|
|
2708
|
+
`, isInline: true, styles: [":host{display:flex;flex-direction:column;align-items:center;gap:var(--theme-space-2, 8px);padding:var(--theme-space-6, 24px) var(--theme-space-4, 16px);text-align:center;color:var(--theme-text-secondary)}.mm-empty-state__title{margin:0;color:var(--theme-text-primary);font-weight:600}:host([data-variant=\"error\"]) .mm-empty-state__title{color:var(--theme-status-error)}.mm-empty-state__text{margin:0;max-width:48ch}.mm-empty-state__retry{margin-top:var(--theme-space-1, 4px)}\n"], dependencies: [{ kind: "component", type: OctoBotComponent, selector: "mm-octobot", inputs: ["animation", "size", "label", "loading", "still", "playOnce"] }, { kind: "component", type: ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2709
|
+
}
|
|
2710
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: EmptyStateComponent, decorators: [{
|
|
2711
|
+
type: Component,
|
|
2712
|
+
args: [{ selector: 'mm-empty-state', imports: [OctoBotComponent, ButtonComponent], template: `
|
|
2713
|
+
@if (figure(); as animation) {
|
|
2714
|
+
<mm-octobot class="mm-empty-state__figure" [animation]="animation" [size]="size()" loading="eager" />
|
|
2715
|
+
}
|
|
2716
|
+
<p class="mm-empty-state__title" [attr.role]="variant() === 'error' ? 'alert' : null">{{ heading() }}</p>
|
|
2717
|
+
@if (text()) {
|
|
2718
|
+
<p class="mm-empty-state__text">{{ text() }}</p>
|
|
2719
|
+
}
|
|
2720
|
+
@if (variant() === 'error' && showRetry()) {
|
|
2721
|
+
<button kendoButton type="button" class="mm-empty-state__retry" (click)="retry.emit()">{{ retryLabel() }}</button>
|
|
2722
|
+
}
|
|
2723
|
+
<ng-content />
|
|
2724
|
+
`, host: {
|
|
2725
|
+
'class': 'mm-empty-state',
|
|
2726
|
+
'[attr.data-variant]': 'variant()'
|
|
2727
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:flex;flex-direction:column;align-items:center;gap:var(--theme-space-2, 8px);padding:var(--theme-space-6, 24px) var(--theme-space-4, 16px);text-align:center;color:var(--theme-text-secondary)}.mm-empty-state__title{margin:0;color:var(--theme-text-primary);font-weight:600}:host([data-variant=\"error\"]) .mm-empty-state__title{color:var(--theme-status-error)}.mm-empty-state__text{margin:0;max-width:48ch}.mm-empty-state__retry{margin-top:var(--theme-space-1, 4px)}\n"] }]
|
|
2728
|
+
}], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: true }] }], text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], showRetry: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRetry", required: false }] }], retryLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "retryLabel", required: false }] }], retry: [{ type: i0.Output, args: ["retry"] }] } });
|
|
2729
|
+
|
|
2730
|
+
/** Context/overflow menu class of `CommandItem.danger` items (AB#5570); styled globally in the list-view SCSS. */
|
|
2731
|
+
const DANGER_MENU_ITEM_CLASS = 'mm-list-view-menu-item--danger';
|
|
2732
|
+
function isOverflowRowActionData(data) {
|
|
2733
|
+
return !!data && data.mmRowAction === true;
|
|
2734
|
+
}
|
|
2735
|
+
/** Menu text: `menuLabel` ("Delete…"), plus the disabled reason ("Delete… — The adapter is deployed"). */
|
|
2736
|
+
function entryMenuText(action) {
|
|
2737
|
+
const text = action.menuLabel || action.label;
|
|
2738
|
+
return isActionDisabled(action) ? `${text} — ${action.disabledReason.trim()}` : text;
|
|
2739
|
+
}
|
|
2740
|
+
function sameEntries(a, b) {
|
|
2741
|
+
return a.length === b.length && a.every((entry, i) => entry.commandItem === b[i].commandItem && entry.rowAction === b[i].rowAction && sameAction(entry.action, b[i].action));
|
|
2742
|
+
}
|
|
1904
2743
|
class ListViewComponent extends CommandBaseService {
|
|
1905
2744
|
cronHumanizer = inject(CronHumanizerService);
|
|
1906
2745
|
_columns = [];
|
|
@@ -1915,11 +2754,23 @@ class ListViewComponent extends CommandBaseService {
|
|
|
1915
2754
|
// context menu (right click)
|
|
1916
2755
|
_contextMenuSelectedRow = null;
|
|
1917
2756
|
_actionMenuItems = [];
|
|
2757
|
+
_rowActions = [];
|
|
2758
|
+
/** Per-row resolved actions; identical results keep their identity (no NG0100, no re-render churn). */
|
|
2759
|
+
rowActionCache = new WeakMap();
|
|
2760
|
+
activatedRoute = inject(ActivatedRoute, { optional: true });
|
|
2761
|
+
routerForActions = inject(Router, { optional: true });
|
|
1918
2762
|
_contextMenuItems = [];
|
|
1919
2763
|
_showRowFilter = false;
|
|
1920
2764
|
/** Indicates if the data source is currently loading data */
|
|
1921
2765
|
isLoading = signal(false, /* @ts-ignore */
|
|
1922
2766
|
...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
|
|
2767
|
+
/**
|
|
2768
|
+
* Whether the data source reported "not loading" at least once (AB#3444). {@link isLoading}
|
|
2769
|
+
* follows the data source one macrotask late (asyncScheduler), so the first render would
|
|
2770
|
+
* otherwise show the empty state while the first fetch is already in flight.
|
|
2771
|
+
*/
|
|
2772
|
+
loadSettled = signal(false, /* @ts-ignore */
|
|
2773
|
+
...(ngDevMode ? [{ debugName: "loadSettled" }] : /* istanbul ignore next */ []));
|
|
1923
2774
|
/**
|
|
1924
2775
|
* The component's own width in pixels, kept current via ResizeObserver.
|
|
1925
2776
|
* `null` until the first measurement (before that, all columns render visible
|
|
@@ -1991,6 +2842,25 @@ class ListViewComponent extends CommandBaseService {
|
|
|
1991
2842
|
this._contextMenuCommandItems = commandItems;
|
|
1992
2843
|
this._contextMenuItems = this.buildMenuItems(commandItems);
|
|
1993
2844
|
}
|
|
2845
|
+
/**
|
|
2846
|
+
* Row actions from the shared action model (AB#5572) — preferred over `actionCommandItems`.
|
|
2847
|
+
* Rendered after the `actionCommandItems` (which keep working through an adapter); together they
|
|
2848
|
+
* get at most {@link maxInlineRowActions} slots, the rest goes into the row's overflow menu.
|
|
2849
|
+
*/
|
|
2850
|
+
set rowActions(actions) {
|
|
2851
|
+
this._rowActions = actions ?? [];
|
|
2852
|
+
this.rowActionCache = new WeakMap();
|
|
2853
|
+
}
|
|
2854
|
+
get rowActions() {
|
|
2855
|
+
return this._rowActions;
|
|
2856
|
+
}
|
|
2857
|
+
/**
|
|
2858
|
+
* Row action slots incl. the overflow ("…") button (guideline §2.2: 3). When the actions do not
|
|
2859
|
+
* fit, the last slot opens the overflow menu with the remaining actions and the context menu items.
|
|
2860
|
+
*/
|
|
2861
|
+
maxInlineRowActions = MM_ROW_ACTIONS_MAX_INLINE;
|
|
2862
|
+
/** An enabled {@link rowActions} action was triggered (inline or from the overflow menu). */
|
|
2863
|
+
rowAction = new EventEmitter();
|
|
1994
2864
|
excelExportFileName = 'Export.xlsx';
|
|
1995
2865
|
pdfExportFileName = 'Export.pdf';
|
|
1996
2866
|
pageable = {
|
|
@@ -2074,9 +2944,22 @@ class ListViewComponent extends CommandBaseService {
|
|
|
2074
2944
|
* case the column has to be wide enough for.
|
|
2075
2945
|
*/
|
|
2076
2946
|
get maxActionButtons() {
|
|
2077
|
-
const
|
|
2078
|
-
|
|
2079
|
-
|
|
2947
|
+
const candidates = this._actionMenuItems.filter(item => !item.separator).length +
|
|
2948
|
+
this._rowActions.filter(a => !a.overflow).length;
|
|
2949
|
+
const forcedMenu = this.hasContextMenuButton || this._rowActions.some(a => a.overflow);
|
|
2950
|
+
const slots = Math.max(1, Math.floor(this.maxInlineRowActions));
|
|
2951
|
+
if (!forcedMenu && candidates <= slots) {
|
|
2952
|
+
return candidates;
|
|
2953
|
+
}
|
|
2954
|
+
return Math.min(candidates, slots - 1) + 1;
|
|
2955
|
+
}
|
|
2956
|
+
/** The context menu is opened from the row's "…" button (not by right click). */
|
|
2957
|
+
get hasContextMenuButton() {
|
|
2958
|
+
return this._contextMenuCommandItems.length > 0 && this.contextMenuType === 'actionMenu';
|
|
2959
|
+
}
|
|
2960
|
+
/** The list renders an actions column / card actions at all. */
|
|
2961
|
+
get hasRowActions() {
|
|
2962
|
+
return this._actionMenuItems.length > 0 || this._rowActions.length > 0 || this.hasContextMenuButton;
|
|
2080
2963
|
}
|
|
2081
2964
|
/**
|
|
2082
2965
|
* The actions column width actually used.
|
|
@@ -2133,6 +3016,58 @@ class ListViewComponent extends CommandBaseService {
|
|
|
2133
3016
|
* the window ever resizing.
|
|
2134
3017
|
*/
|
|
2135
3018
|
collapseCommandsBelow = 900;
|
|
3019
|
+
/**
|
|
3020
|
+
* Tells the list that the host holds filters of its own (quick-view bars,
|
|
3021
|
+
* app-side field filters) that a reset would clear. The toolbar's reset
|
|
3022
|
+
* button only shows while something is filtered, and the list cannot see
|
|
3023
|
+
* those host filters by itself.
|
|
3024
|
+
*/
|
|
3025
|
+
hasExternalFilters = false;
|
|
3026
|
+
/**
|
|
3027
|
+
* Empty state of the list (AB#3444): when set, a list without records shows the shared
|
|
3028
|
+
* `mm-empty-state` — one OctoBot `idle` with {@link MmListEmptyState.title} / `text` — instead
|
|
3029
|
+
* of the plain "No records available." line; while a search, row filter or host filter
|
|
3030
|
+
* ({@link hasExternalFilters}) is active it shows OctoBot `look` with the "no results" texts.
|
|
3031
|
+
* Nothing is shown while the list loads (including before the first fetch answered). The figure appears once per list, never in rows.
|
|
3032
|
+
*/
|
|
3033
|
+
emptyState = null;
|
|
3034
|
+
/**
|
|
3035
|
+
* Fully custom empty state (wins over {@link emptyState}). Context: `$implicit` / `filtered`
|
|
3036
|
+
* is `true` while a search or filter is active.
|
|
3037
|
+
*/
|
|
3038
|
+
emptyTemplate = null;
|
|
3039
|
+
/**
|
|
3040
|
+
* Whether a search, row filter or host filter narrows the rows right now (sorting does not),
|
|
3041
|
+
* i.e. an empty list means "no results" rather than "no records yet".
|
|
3042
|
+
*/
|
|
3043
|
+
get isFiltered() {
|
|
3044
|
+
if (this.hasExternalFilters || this.searchValue.trim().length > 0) {
|
|
3045
|
+
return true;
|
|
3046
|
+
}
|
|
3047
|
+
return (this.dataBindingDirective?.currentState?.filter?.filters?.length ?? 0) > 0;
|
|
3048
|
+
}
|
|
3049
|
+
get emptyContext() {
|
|
3050
|
+
const filtered = this.isFiltered;
|
|
3051
|
+
return { $implicit: filtered, filtered };
|
|
3052
|
+
}
|
|
3053
|
+
/**
|
|
3054
|
+
* Whether anything narrows or reorders the default view right now: a row
|
|
3055
|
+
* filter, a column sort, free-text search, or host-side filters announced
|
|
3056
|
+
* through {@link hasExternalFilters}. Paging is deliberately not counted —
|
|
3057
|
+
* the pager already gets back to page 1.
|
|
3058
|
+
*/
|
|
3059
|
+
get hasActiveFilters() {
|
|
3060
|
+
if (this.hasExternalFilters || this.searchValue.trim().length > 0) {
|
|
3061
|
+
return true;
|
|
3062
|
+
}
|
|
3063
|
+
const state = this.dataBindingDirective?.currentState;
|
|
3064
|
+
if (!state) {
|
|
3065
|
+
return false;
|
|
3066
|
+
}
|
|
3067
|
+
const hasRowFilter = (state.filter?.filters?.length ?? 0) > 0;
|
|
3068
|
+
const hasSort = (state.sort ?? []).some(descriptor => !!descriptor.dir);
|
|
3069
|
+
return hasRowFilter || hasSort;
|
|
3070
|
+
}
|
|
2136
3071
|
/** True while the command buttons are collapsed into the overflow menu. */
|
|
2137
3072
|
get commandsCollapsed() {
|
|
2138
3073
|
const width = this.containerWidth();
|
|
@@ -2142,15 +3077,22 @@ class ListViewComponent extends CommandBaseService {
|
|
|
2142
3077
|
* The commands available right now, in toolbar order. The row filter is
|
|
2143
3078
|
* omitted in card mode: cards have no column headers for a filter row to
|
|
2144
3079
|
* appear in, and the grid already drops `filterable` there — leaving the
|
|
2145
|
-
* button visible offered to toggle something that cannot exist. Reset
|
|
2146
|
-
*
|
|
3080
|
+
* button visible offered to toggle something that cannot exist. Reset shows
|
|
3081
|
+
* only while something is actually filtered, sorted or searched (see
|
|
3082
|
+
* {@link hasActiveFilters}); on the default view there is nothing to reset,
|
|
3083
|
+
* so the button would only be noise. It is independent of card mode, so a
|
|
3084
|
+
* filter set before the switch can still be cleared.
|
|
2147
3085
|
*/
|
|
2148
3086
|
get toolbarCommands() {
|
|
2149
3087
|
const commands = [];
|
|
2150
3088
|
if (this.rowFilterEnabled && !this.isCardMode) {
|
|
2151
3089
|
commands.push({ id: 'rowFilter', text: this._messages.showRowFilter, svgIcon: this.filterIcon });
|
|
2152
3090
|
}
|
|
2153
|
-
commands.push({ id: 'excel', text: this._messages.exportToExcel, svgIcon: this.excelSVG }, { id: 'pdf', text: this._messages.exportToPdf, svgIcon: this.pdfSVG }
|
|
3091
|
+
commands.push({ id: 'excel', text: this._messages.exportToExcel, svgIcon: this.excelSVG }, { id: 'pdf', text: this._messages.exportToPdf, svgIcon: this.pdfSVG });
|
|
3092
|
+
if (this.hasActiveFilters) {
|
|
3093
|
+
commands.push({ id: 'reset', text: this._messages.resetFilters, svgIcon: this.resetFilterIcon });
|
|
3094
|
+
}
|
|
3095
|
+
commands.push({ id: 'refresh', text: this._messages.refreshData, svgIcon: this.refreshIcon });
|
|
2154
3096
|
return commands;
|
|
2155
3097
|
}
|
|
2156
3098
|
/**
|
|
@@ -2207,9 +3149,13 @@ class ListViewComponent extends CommandBaseService {
|
|
|
2207
3149
|
*/
|
|
2208
3150
|
rowClass;
|
|
2209
3151
|
defaultRowClass = () => ({});
|
|
2210
|
-
_messages =
|
|
3152
|
+
_messages = resolveListViewMessages(undefined);
|
|
3153
|
+
/**
|
|
3154
|
+
* Translated messages. Partial: members that are not supplied (or are `undefined`) keep the
|
|
3155
|
+
* English default from `DEFAULT_LIST_VIEW_MESSAGES`.
|
|
3156
|
+
*/
|
|
2211
3157
|
set messages(value) {
|
|
2212
|
-
this._messages =
|
|
3158
|
+
this._messages = resolveListViewMessages(value);
|
|
2213
3159
|
}
|
|
2214
3160
|
get messages() {
|
|
2215
3161
|
return this._messages;
|
|
@@ -2257,6 +3203,9 @@ class ListViewComponent extends CommandBaseService {
|
|
|
2257
3203
|
if (this.dataBindingDirective) {
|
|
2258
3204
|
this.dataBindingDirective.isLoading$.pipe(observeOn(asyncScheduler), takeUntil(this.destroy$)).subscribe(loading => {
|
|
2259
3205
|
this.isLoading.set(loading);
|
|
3206
|
+
if (!loading) {
|
|
3207
|
+
this.loadSettled.set(true);
|
|
3208
|
+
}
|
|
2260
3209
|
// Measure the fit-to-height page size only ONCE, when data first
|
|
2261
3210
|
// renders (initial default → fitted). After that, page-size changes come
|
|
2262
3211
|
// solely from the ResizeObserver (genuine viewport/density changes).
|
|
@@ -2425,7 +3374,7 @@ class ListViewComponent extends CommandBaseService {
|
|
|
2425
3374
|
if (this.showCheckboxColumn()) {
|
|
2426
3375
|
fixedWidth += this.checkboxColumnWidth;
|
|
2427
3376
|
}
|
|
2428
|
-
if (this.
|
|
3377
|
+
if (this.hasRowActions) {
|
|
2429
3378
|
fixedWidth += this.effectiveActionsColumnWidth;
|
|
2430
3379
|
}
|
|
2431
3380
|
const autoColumns = visibleColumns.filter(c => c.width === undefined);
|
|
@@ -2457,6 +3406,39 @@ class ListViewComponent extends CommandBaseService {
|
|
|
2457
3406
|
getToolbarItemDisabled(commandItem) {
|
|
2458
3407
|
return CommandBaseService.getIsDisabled(commandItem, this._selectedRows);
|
|
2459
3408
|
}
|
|
3409
|
+
/**
|
|
3410
|
+
* Accessible name of a host toolbar action. A button with a visible text is named by that
|
|
3411
|
+
* text; an icon-only one (no text) gets its tooltip as `aria-label`, because `title` alone is
|
|
3412
|
+
* not reliably announced (action guideline §2.2: icon-only = tooltip + aria-label).
|
|
3413
|
+
*/
|
|
3414
|
+
toolbarItemAriaLabel(commandItem) {
|
|
3415
|
+
if (commandItem.text?.trim()) {
|
|
3416
|
+
return null;
|
|
3417
|
+
}
|
|
3418
|
+
return commandItem.tooltip?.trim() ? commandItem.tooltip : null;
|
|
3419
|
+
}
|
|
3420
|
+
buttonAttributesCache = new Map();
|
|
3421
|
+
static NO_BUTTON_ATTRIBUTES = {};
|
|
3422
|
+
/**
|
|
3423
|
+
* `buttonAttributes` for Kendo split / dropdown buttons: same rule as
|
|
3424
|
+
* {@link toolbarItemAriaLabel}, cached per label so change detection sees a stable object.
|
|
3425
|
+
*/
|
|
3426
|
+
toolbarItemButtonAttributes(commandItem) {
|
|
3427
|
+
const label = this.toolbarItemAriaLabel(commandItem);
|
|
3428
|
+
return label ? this.ariaLabelAttributes(label) : ListViewComponent.NO_BUTTON_ATTRIBUTES;
|
|
3429
|
+
}
|
|
3430
|
+
/** `buttonAttributes` of the icon-only menu the commands collapse into. */
|
|
3431
|
+
get commandsMenuButtonAttributes() {
|
|
3432
|
+
return this.ariaLabelAttributes(this._messages.commands);
|
|
3433
|
+
}
|
|
3434
|
+
ariaLabelAttributes(label) {
|
|
3435
|
+
let attributes = this.buttonAttributesCache.get(label);
|
|
3436
|
+
if (!attributes) {
|
|
3437
|
+
attributes = { 'aria-label': label };
|
|
3438
|
+
this.buttonAttributesCache.set(label, attributes);
|
|
3439
|
+
}
|
|
3440
|
+
return attributes;
|
|
3441
|
+
}
|
|
2460
3442
|
getValue(element, column) {
|
|
2461
3443
|
if (column.field.indexOf('.') === -1) {
|
|
2462
3444
|
return element[column.field];
|
|
@@ -2509,6 +3491,8 @@ class ListViewComponent extends CommandBaseService {
|
|
|
2509
3491
|
case 'boolean':
|
|
2510
3492
|
case 'date':
|
|
2511
3493
|
return column.dataType;
|
|
3494
|
+
case 'booleanIcon':
|
|
3495
|
+
return 'boolean';
|
|
2512
3496
|
case 'numericRange':
|
|
2513
3497
|
case 'progressBar':
|
|
2514
3498
|
return 'numeric';
|
|
@@ -2518,6 +3502,49 @@ class ListViewComponent extends CommandBaseService {
|
|
|
2518
3502
|
return 'text';
|
|
2519
3503
|
}
|
|
2520
3504
|
}
|
|
3505
|
+
/**
|
|
3506
|
+
* State of a `booleanIcon` cell (AB#5623): `'true'` / `'false'` for booleans (also the strings
|
|
3507
|
+
* "true" / "false"), `null` for an empty or non-boolean value (empty cell).
|
|
3508
|
+
*/
|
|
3509
|
+
booleanIconState(element, column) {
|
|
3510
|
+
const value = this.getValue(element, column);
|
|
3511
|
+
if (value === true || value === 'true')
|
|
3512
|
+
return 'true';
|
|
3513
|
+
if (value === false || value === 'false')
|
|
3514
|
+
return 'false';
|
|
3515
|
+
return null;
|
|
3516
|
+
}
|
|
3517
|
+
/** Icon of a `booleanIcon` cell: the column's `booleanIcons`, else check / x; `null` = no icon. */
|
|
3518
|
+
booleanIcon(column, state) {
|
|
3519
|
+
const icons = column.booleanIcons;
|
|
3520
|
+
if (state === 'true') {
|
|
3521
|
+
return icons?.true ?? checkIcon;
|
|
3522
|
+
}
|
|
3523
|
+
return icons?.false === null ? null : icons?.false ?? xIcon;
|
|
3524
|
+
}
|
|
3525
|
+
/** Accessible name of a `booleanIcon` cell: "<column>: Yes|No" from the messages (AB#5623). */
|
|
3526
|
+
booleanIconLabel(column, state) {
|
|
3527
|
+
const value = state === 'true' ? this._messages.booleanYes : this._messages.booleanNo;
|
|
3528
|
+
const name = this.getDisplayName(column) ?? '';
|
|
3529
|
+
return name ? this._messages.booleanIconLabel.replace('{column}', name).replace('{value}', value) : value;
|
|
3530
|
+
}
|
|
3531
|
+
/** Input attributes of a read-only boolean cell, cached per column and name (AB#5621). */
|
|
3532
|
+
booleanCellAttributeCache = new WeakMap();
|
|
3533
|
+
/**
|
|
3534
|
+
* Input attributes of a `boolean` cell's read-only checkbox: its accessible name is the column's
|
|
3535
|
+
* display name (AB#5621), so a screen reader announces "<column>, checked" instead of an unnamed
|
|
3536
|
+
* checkbox.
|
|
3537
|
+
*/
|
|
3538
|
+
booleanCellAttributes(column) {
|
|
3539
|
+
const name = this.getDisplayName(column) ?? '';
|
|
3540
|
+
const cached = this.booleanCellAttributeCache.get(column);
|
|
3541
|
+
if (cached && cached.name === name) {
|
|
3542
|
+
return cached.attributes;
|
|
3543
|
+
}
|
|
3544
|
+
const attributes = name ? { 'aria-label': name } : {};
|
|
3545
|
+
this.booleanCellAttributeCache.set(column, { name, attributes });
|
|
3546
|
+
return attributes;
|
|
3547
|
+
}
|
|
2521
3548
|
/**
|
|
2522
3549
|
* Gets all status field configurations for a statusIcons column.
|
|
2523
3550
|
* Returns an array of StatusFieldConfig, whether the column uses single or multi-field configuration.
|
|
@@ -2697,10 +3724,21 @@ class ListViewComponent extends CommandBaseService {
|
|
|
2697
3724
|
this._selectedRows = [];
|
|
2698
3725
|
}
|
|
2699
3726
|
async onContextMenuSelect(_event) {
|
|
2700
|
-
const
|
|
2701
|
-
if (!
|
|
3727
|
+
const data = _event.item?.data?.data;
|
|
3728
|
+
if (!data) {
|
|
3729
|
+
return;
|
|
3730
|
+
}
|
|
3731
|
+
if (isOverflowRowActionData(data)) {
|
|
3732
|
+
const row = this._actionMenuSelectedRow;
|
|
3733
|
+
if (data.entry.commandItem) {
|
|
3734
|
+
await this.navigateAsync(data.entry.commandItem, row);
|
|
3735
|
+
}
|
|
3736
|
+
else {
|
|
3737
|
+
await this.triggerRowAction(data.entry, row);
|
|
3738
|
+
}
|
|
2702
3739
|
return;
|
|
2703
3740
|
}
|
|
3741
|
+
const commandItem = data;
|
|
2704
3742
|
if (this._actionMenuSelectedRow) {
|
|
2705
3743
|
await this.navigateAsync(commandItem, this._actionMenuSelectedRow);
|
|
2706
3744
|
}
|
|
@@ -2754,6 +3792,21 @@ class ListViewComponent extends CommandBaseService {
|
|
|
2754
3792
|
}
|
|
2755
3793
|
await this.navigateAsync(commandItem, dataItem);
|
|
2756
3794
|
}
|
|
3795
|
+
/**
|
|
3796
|
+
* Row field naming a row for assistive technology (row menu "Actions for <name>", action
|
|
3797
|
+
* buttons "<action> <name>"). Falls back to `rtWellKnownName`, then `rtId`. A dotted path
|
|
3798
|
+
* (`contact.displayName`) reads a nested value (AB#5623).
|
|
3799
|
+
*/
|
|
3800
|
+
rowLabelField = 'name';
|
|
3801
|
+
/** Human name of a row (`rowLabelField`, `rtWellKnownName`, `rtId`), or '' when unknown. */
|
|
3802
|
+
rowLabel(dataItem) {
|
|
3803
|
+
return resolveListRowLabel(dataItem, this.rowLabelField);
|
|
3804
|
+
}
|
|
3805
|
+
/** Accessible name and tooltip of the row menu button. */
|
|
3806
|
+
rowActionsLabel(dataItem) {
|
|
3807
|
+
const name = this.rowLabel(dataItem);
|
|
3808
|
+
return name ? this.messages.rowActionsFor.replace('{name}', name) : this.messages.rowActions;
|
|
3809
|
+
}
|
|
2757
3810
|
getMenuItemVisible(menuItem, dataItem) {
|
|
2758
3811
|
const commandItem = menuItem.data;
|
|
2759
3812
|
if (commandItem) {
|
|
@@ -2772,7 +3825,19 @@ class ListViewComponent extends CommandBaseService {
|
|
|
2772
3825
|
this._actionMenuSelectedRow = dataItem;
|
|
2773
3826
|
// Rebuild context menu items with updated disabled state for the current row.
|
|
2774
3827
|
// A new array reference is needed so Kendo's kendoMenuHierarchyBinding detects the change.
|
|
2775
|
-
|
|
3828
|
+
const overflow = this.rowActionsView(dataItem).menu.map((entry) => ({
|
|
3829
|
+
text: entryMenuText(entry.action),
|
|
3830
|
+
svgIcon: entry.action.icon,
|
|
3831
|
+
data: { mmRowAction: true, entry },
|
|
3832
|
+
disabled: isActionDisabled(entry.action),
|
|
3833
|
+
cssClass: entry.action.danger ? DANGER_MENU_ITEM_CLASS : undefined,
|
|
3834
|
+
}));
|
|
3835
|
+
const contextItems = this.contextMenuType === 'actionMenu'
|
|
3836
|
+
? this.buildContextMenuItemsWithDisabledState(this._contextMenuCommandItems, dataItem)
|
|
3837
|
+
: [];
|
|
3838
|
+
this._contextMenuItems = overflow.length && contextItems.length
|
|
3839
|
+
? [...overflow, { separator: true }, ...contextItems]
|
|
3840
|
+
: [...overflow, ...contextItems];
|
|
2776
3841
|
this.gridContextMenu?.show({
|
|
2777
3842
|
left: this.clampContextMenuX(e.pageX),
|
|
2778
3843
|
top: e.pageY,
|
|
@@ -2795,6 +3860,7 @@ class ListViewComponent extends CommandBaseService {
|
|
|
2795
3860
|
data: commandItem,
|
|
2796
3861
|
items: childMenuItems,
|
|
2797
3862
|
disabled: CommandBaseService.getIsDisabled(commandItem, dataItem),
|
|
3863
|
+
cssClass: commandItem.danger ? DANGER_MENU_ITEM_CLASS : undefined,
|
|
2798
3864
|
});
|
|
2799
3865
|
}
|
|
2800
3866
|
}
|
|
@@ -2803,6 +3869,95 @@ class ListViewComponent extends CommandBaseService {
|
|
|
2803
3869
|
onContextMenuClosed(_event) {
|
|
2804
3870
|
this._contextMenuSelectedRow = null;
|
|
2805
3871
|
this._actionMenuSelectedRow = null;
|
|
3872
|
+
// Back to the static items: `hasContextMenuButton` and the column width rely on them.
|
|
3873
|
+
this._contextMenuItems = this.buildMenuItems(this._contextMenuCommandItems);
|
|
3874
|
+
}
|
|
3875
|
+
/**
|
|
3876
|
+
* Row actions of one row (AB#5572): `actionCommandItems` (adapter) followed by `rowActions`,
|
|
3877
|
+
* split into inline buttons and overflow-menu entries (max {@link maxInlineRowActions} slots,
|
|
3878
|
+
* context menu items and `overflow: true` actions force the "…" button).
|
|
3879
|
+
*/
|
|
3880
|
+
rowActionsView(dataItem) {
|
|
3881
|
+
const loadingReason = this.isLoading() ? this._messages.rowActionsLoading : null;
|
|
3882
|
+
const resolved = [];
|
|
3883
|
+
for (const commandItem of this._actionCommandItems) {
|
|
3884
|
+
if (commandItem.type === 'separator' || !CommandBaseService.getIsVisibleSync(commandItem, dataItem)) {
|
|
3885
|
+
continue;
|
|
3886
|
+
}
|
|
3887
|
+
resolved.push({ commandItem, action: this.commandItemAction(commandItem, dataItem, loadingReason) });
|
|
3888
|
+
}
|
|
3889
|
+
for (const rowAction of this._rowActions) {
|
|
3890
|
+
const action = resolveListRowAction(rowAction, dataItem);
|
|
3891
|
+
if (!isActionVisible(action)) {
|
|
3892
|
+
continue;
|
|
3893
|
+
}
|
|
3894
|
+
resolved.push({ rowAction, action: loadingReason && !isActionDisabled(action) ? { ...action, disabledReason: loadingReason } : action });
|
|
3895
|
+
}
|
|
3896
|
+
const candidates = resolved.filter(e => !e.action.overflow);
|
|
3897
|
+
const forced = resolved.filter(e => e.action.overflow);
|
|
3898
|
+
const slots = Math.max(1, Math.floor(this.maxInlineRowActions));
|
|
3899
|
+
const needsMenu = forced.length > 0 || candidates.length > slots || this.hasContextMenuButton;
|
|
3900
|
+
const inlineCount = needsMenu ? Math.max(0, slots - 1) : candidates.length;
|
|
3901
|
+
const view = {
|
|
3902
|
+
inline: candidates.slice(0, inlineCount),
|
|
3903
|
+
menu: [...candidates.slice(inlineCount), ...forced],
|
|
3904
|
+
hasMenu: needsMenu,
|
|
3905
|
+
};
|
|
3906
|
+
return this.stableView(dataItem, view);
|
|
3907
|
+
}
|
|
3908
|
+
/** Accessible name of the row action group: "Actions for <row>". */
|
|
3909
|
+
rowActionsGroupLabel(dataItem) {
|
|
3910
|
+
return this.rowActionsLabel(dataItem);
|
|
3911
|
+
}
|
|
3912
|
+
async onInlineRowAction(entry, dataItem) {
|
|
3913
|
+
if (entry.commandItem) {
|
|
3914
|
+
// A routerLink-rendered action navigated already; CommandItems never use MmAction.link.
|
|
3915
|
+
await this.navigateAsync(entry.commandItem, dataItem);
|
|
3916
|
+
return;
|
|
3917
|
+
}
|
|
3918
|
+
// Inline link actions navigate via their own routerLink.
|
|
3919
|
+
await this.triggerRowAction(entry, dataItem, !!entry.action.link);
|
|
3920
|
+
}
|
|
3921
|
+
async triggerRowAction(entry, row, navigated = false) {
|
|
3922
|
+
const action = entry.action;
|
|
3923
|
+
if (isActionDisabled(action)) {
|
|
3924
|
+
return;
|
|
3925
|
+
}
|
|
3926
|
+
if (!navigated && action.link && this.routerForActions) {
|
|
3927
|
+
const commands = typeof action.link.commands === 'string' ? [action.link.commands] : [...action.link.commands];
|
|
3928
|
+
void this.routerForActions.navigate(commands, { queryParams: action.link.queryParams, relativeTo: this.activatedRoute ?? undefined });
|
|
3929
|
+
}
|
|
3930
|
+
this.rowAction.emit({ id: action.id, action, row });
|
|
3931
|
+
await entry.rowAction?.run?.(row);
|
|
3932
|
+
}
|
|
3933
|
+
/** `CommandItem` → `MmAction` adapter (AB#5572): `isDisabled` → reason, `danger`, icon, text. */
|
|
3934
|
+
commandItemAction(commandItem, dataItem, loadingReason) {
|
|
3935
|
+
let disabledReason = null;
|
|
3936
|
+
if (CommandBaseService.getIsDisabled(commandItem, dataItem)) {
|
|
3937
|
+
const reason = typeof commandItem.disabledReason === 'function'
|
|
3938
|
+
? commandItem.disabledReason(dataItem)
|
|
3939
|
+
: commandItem.disabledReason;
|
|
3940
|
+
disabledReason = reason && reason.trim() ? reason : this._messages.rowActionUnavailable;
|
|
3941
|
+
}
|
|
3942
|
+
return {
|
|
3943
|
+
id: commandItem.id,
|
|
3944
|
+
label: commandItem.text || commandItem.tooltip || commandItem.id,
|
|
3945
|
+
icon: commandItem.svgIcon,
|
|
3946
|
+
danger: !!commandItem.danger,
|
|
3947
|
+
disabledReason: disabledReason ?? loadingReason,
|
|
3948
|
+
};
|
|
3949
|
+
}
|
|
3950
|
+
stableView(dataItem, view) {
|
|
3951
|
+
if (typeof dataItem !== 'object' || dataItem === null) {
|
|
3952
|
+
return view;
|
|
3953
|
+
}
|
|
3954
|
+
const previous = this.rowActionCache.get(dataItem);
|
|
3955
|
+
if (previous && sameEntries(previous.inline, view.inline) && sameEntries(previous.menu, view.menu)
|
|
3956
|
+
&& previous.hasMenu === view.hasMenu) {
|
|
3957
|
+
return previous;
|
|
3958
|
+
}
|
|
3959
|
+
this.rowActionCache.set(dataItem, view);
|
|
3960
|
+
return view;
|
|
2806
3961
|
}
|
|
2807
3962
|
async onToolbarCommand(commandItem) {
|
|
2808
3963
|
if (this._selectedRows.length === 1) {
|
|
@@ -2835,7 +3990,8 @@ class ListViewComponent extends CommandBaseService {
|
|
|
2835
3990
|
text: commandItem.text,
|
|
2836
3991
|
svgIcon: commandItem.svgIcon,
|
|
2837
3992
|
data: commandItem,
|
|
2838
|
-
items: childMenuItems
|
|
3993
|
+
items: childMenuItems,
|
|
3994
|
+
cssClass: commandItem.danger ? DANGER_MENU_ITEM_CLASS : undefined,
|
|
2839
3995
|
});
|
|
2840
3996
|
}
|
|
2841
3997
|
}
|
|
@@ -2856,7 +4012,7 @@ class ListViewComponent extends CommandBaseService {
|
|
|
2856
4012
|
*/
|
|
2857
4013
|
commandsIcon = slidersIcon;
|
|
2858
4014
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ListViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2859
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: ListViewComponent, isStandalone: true, selector: "mm-list-view", inputs: { pageSize: "pageSize", skip: "skip", autoPageSize: "autoPageSize", rowIsClickable: "rowIsClickable", showRowCheckBoxes: "showRowCheckBoxes", showRowSelectAllCheckBox: "showRowSelectAllCheckBox", contextMenuType: "contextMenuType", leftToolbarActions: "leftToolbarActions", rightToolbarActions: "rightToolbarActions", actionCommandItems: "actionCommandItems", contextMenuCommandItems: "contextMenuCommandItems", excelExportFileName: "excelExportFileName", pdfExportFileName: "pdfExportFileName", pageable: "pageable", sortable: "sortable", rowFilterEnabled: "rowFilterEnabled", searchTextBoxEnabled: "searchTextBoxEnabled", persistListState: "persistListState", listStateKey: "listStateKey", resizable: "resizable", actionsColumnWidth: "actionsColumnWidth", hideCheckboxesBelow: "hideCheckboxesBelow", cardModeBelow: "cardModeBelow", collapseCommandsBelow: "collapseCommandsBelow", rowClass: "rowClass", messages: "messages", selectable: "selectable", columns: "columns" }, outputs: { rowClicked: "rowClicked", resetFilters: "resetFilters" }, host: { properties: { "class.mm-list-view-narrow": "this.isNarrow", "class.mm-list-view-cards": "this.isCardMode" } }, queries: [{ propertyName: "filtersTemplate", first: true, predicate: ListViewFiltersDirective, descendants: true }], viewQueries: [{ propertyName: "gridComponent", first: true, predicate: GridComponent, descendants: true }, { propertyName: "dataBindingDirective", first: true, predicate: MmListViewDataBindingDirective, descendants: true }, { propertyName: "gridContextMenu", first: true, predicate: ["gridmenu"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<!-- Shared cell renderer (AB#4930): used by the regular grid columns AND the\n card-mode body lines, so both render every dataType identically. -->\n<ng-template #cellContent let-element=\"element\" let-column=\"column\">\n @if (column.formatter) {\n {{ getFormattedValue(element, column) }}\n } @else {\n @switch (column.dataType) {\n @case ('boolean') {\n <kendo-checkbox type=\"checkbox\" [checkedState]=\"$any(getValue(element, column))\" [disabled]=\"true\" />\n }\n @case ('iso8601') {\n {{ $any(getValue(element, column)) | date:column.format }}\n }\n @case ('bytes') {\n {{ $any(getValue(element, column)) | bytesToSize }}\n }\n @case ('statusIcons') {\n <span class=\"status-icons-cell\">\n @for (fieldConfig of getStatusFields(column); track fieldConfig.field) {\n @if (getStatusIconMapping(element, fieldConfig); as mapping) {\n <span\n class=\"status-icon\"\n [title]=\"mapping.tooltip\"\n [style.color]=\"mapping.color\">\n <kendo-svg-icon [icon]=\"mapping.icon\"></kendo-svg-icon>\n </span>\n }\n }\n </span>\n }\n @case ('cronExpression') {\n <span class=\"cron-expression-cell\">\n <code class=\"cron-expression\">{{ getValue(element, column) }}</code>\n <span class=\"cron-description\">{{ getCronHumanReadable(getValue(element, column)) }}</span>\n </span>\n }\n @case ('numeric') {\n {{ $any(getValue(element, column)) | number:column.format }}\n }\n @case ('progressBar') {\n <span class=\"progress-bar-cell\">\n <span class=\"progress-bar-track\">\n <span class=\"progress-bar-fill\" [style.width.%]=\"getValue(element, column)\"></span>\n </span>\n <span class=\"progress-bar-label\">{{ getValue(element, column) }}%</span>\n </span>\n }\n @case ('badge') {\n @if (getBadgeMapping(element, column); as badge) {\n <span\n class=\"badge-cell\"\n [title]=\"badge.tooltip ?? ''\"\n [style.color]=\"badge.color\"\n [style.background]=\"badge.backgroundColor\"\n [style.borderColor]=\"badge.borderColor ?? badge.backgroundColor\">\n {{ badge.label ?? getValue(element, column) }}\n </span>\n } @else if (!column.badgeHideUnmapped) {\n <span class=\"badge-cell badge-cell-default\">{{ getValue(element, column) }}</span>\n }\n }\n @case ('component') {\n @if (column.cellComponent) {\n <ng-container *ngComponentOutlet=\"column.cellComponent; inputs: getCellInputs(element, column)\" />\n }\n }\n @default {\n {{ getValue(element, column) }}\n }\n }\n }\n</ng-template>\n\n<!-- Row actions (AB#4930): shared by the actions column and the card header. -->\n<ng-template #rowActions let-dataItem=\"dataItem\">\n @if (_actionMenuItems.length > 0) {\n @for (menuItem of _actionMenuItems; track menuItem.text) {\n @if (!menuItem.separator && getMenuItemVisible(menuItem, dataItem)) {\n <button kendoButton themeColor=\"primary\" fillMode=\"flat\"\n [svgIcon]=\"menuItem.svgIcon!\"\n [title]=\"menuItem.text ?? ''\"\n [disabled]=\"getMenuItemDisabled(menuItem, dataItem) || isLoading()\"\n (click)=\"onSelectOptionActionItem($event, dataItem, menuItem)\">\n </button>\n }\n }\n }\n\n @if (_contextMenuItems.length > 0 && contextMenuType == 'actionMenu') {\n <button kendoButton themeColor=\"primary\" fillMode=\"flat\" [svgIcon]=\"moreVerticalIcon\" [disabled]=\"isLoading()\" (click)=\"onContextMenu(dataItem, $event)\"></button>\n }\n</ng-template>\n\n<kendo-grid\n mmListViewDataBinding\n [loading]=\"isLoading()\"\n [pageSize]=\"effectivePageSize()\" [selectable]=\"selectable\" (pageChange)=\"onPageChange($event)\"\n [skip]=\"skip\" (selectionChange)=\"onRowSelect($event)\" (cellClick)=\"onCellClick($event)\"\n [pageable]=\"effectivePageable\"\n [sortable]=\"sortable\"\n [filterable]=\"_showRowFilter && !isCardMode\"\n [resizable]=\"resizable\"\n [rowClass]=\"rowClass ?? defaultRowClass\"\n>\n <kendo-grid-messages\n [pagerItemsPerPage]=\"_messages.pagerItemsPerPage\"\n [pagerOf]=\"_messages.pagerOf\"\n [pagerItems]=\"_messages.pagerItems\"\n [pagerPage]=\"_messages.pagerPage\"\n [pagerFirstPage]=\"_messages.pagerFirstPage\"\n [pagerLastPage]=\"_messages.pagerLastPage\"\n [pagerPreviousPage]=\"_messages.pagerPreviousPage\"\n [pagerNextPage]=\"_messages.pagerNextPage\"\n [noRecords]=\"_messages.noRecords\"\n ></kendo-grid-messages>\n <ng-template kendoGridToolbarTemplate>\n\n @for (commandItem of leftToolbarActions; track commandItem.id) {\n @if (commandItem) {\n @switch (commandItem.type) {\n @case ('link') {\n @if (commandItem.children && commandItem.children.length > 0 && commandItem.onClick) {\n <kendo-splitbutton\n [data]=\"commandItem.children\"\n [svgIcon]=\"$any(commandItem.svgIcon)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n [textField]=\"'text'\"\n themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n (buttonClick)=\"onToolbarCommand(commandItem)\"\n (itemClick)=\"onToolbarDropdownItemClick($event)\">\n {{ commandItem.text }}\n </kendo-splitbutton>\n } @else if (commandItem.children && commandItem.children.length > 0) {\n <kendo-dropdownbutton\n [data]=\"commandItem.children\"\n [svgIcon]=\"$any(commandItem.svgIcon)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n [textField]=\"'text'\"\n themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n (itemClick)=\"onToolbarDropdownItemClick($event)\">\n {{ commandItem.text }}\n </kendo-dropdownbutton>\n } @else if (commandItem.svgIcon) {\n <button kendoTooltip kendoButton themeColor=\"primary\" [svgIcon]=\"commandItem.svgIcon\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n (click)=\"onToolbarCommand(commandItem)\">{{ commandItem.text }}\n </button>\n } @else {\n <button kendoTooltip kendoButton themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n (click)=\"onToolbarCommand(commandItem)\">{{ commandItem.text }}\n </button>\n }\n }\n @case ('separator'){\n <kendo-separator></kendo-separator>\n }\n }\n }\n }\n\n <!-- Host scope/filter controls (view switches and the like). They steer the\n table, so they sit in the table's own band instead of a second strip\n stacked above the grid. -->\n @if (filtersTemplate) {\n <!-- A rule between the host's actions and its filters: both sit on the\n left of the same band, but one acts on records and the other changes\n what the list shows. Only drawn when actions precede them. -->\n @if (leftToolbarActions.length > 0) {\n <kendo-separator class=\"mm-toolbar-filters-separator\"></kendo-separator>\n }\n <div class=\"mm-toolbar-filters\">\n <ng-container [ngTemplateOutlet]=\"filtersTemplate.templateRef\"></ng-container>\n </div>\n }\n\n <kendo-grid-spacer></kendo-grid-spacer>\n @if (searchTextBoxEnabled) {\n <input\n class=\"k-textbox k-input k-input-md k-rounded-md mm-search-input\"\n [placeholder]=\"_messages.searchPlaceholder\"\n [value]=\"searchValue\"\n (input)=\"onFilter($any($event.target).value || null)\"\n />\n }\n\n @for (commandItem of rightToolbarActions; track commandItem.id) {\n @if (commandItem) {\n @switch (commandItem.type) {\n @case ('link') {\n @if (commandItem.children && commandItem.children.length > 0 && commandItem.onClick) {\n <kendo-splitbutton\n [data]=\"commandItem.children\"\n [svgIcon]=\"$any(commandItem.svgIcon)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n [textField]=\"'text'\"\n themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n (buttonClick)=\"onToolbarCommand(commandItem)\"\n (itemClick)=\"onToolbarDropdownItemClick($event)\">\n {{ commandItem.text }}\n </kendo-splitbutton>\n } @else if (commandItem.children && commandItem.children.length > 0) {\n <kendo-dropdownbutton\n [data]=\"commandItem.children\"\n [svgIcon]=\"$any(commandItem.svgIcon)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n [textField]=\"'text'\"\n themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n (itemClick)=\"onToolbarDropdownItemClick($event)\">\n {{ commandItem.text }}\n </kendo-dropdownbutton>\n } @else if (commandItem.svgIcon) {\n <button kendoTooltip kendoButton themeColor=\"primary\" [svgIcon]=\"commandItem.svgIcon\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n (click)=\"onToolbarCommand(commandItem)\">{{ commandItem.text }}\n </button>\n } @else {\n <button kendoTooltip kendoButton themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n (click)=\"onToolbarCommand(commandItem)\">{{ commandItem.text }}\n </button>\n }\n }\n @case ('separator'){\n <kendo-separator></kendo-separator>\n }\n }\n }\n }\n\n <!-- Commands: laid out while there is room, collapsed into one menu once\n the list gets narrow. Same behaviour in every host app \u2014 a list this\n wide has space for five buttons, and a list this narrow does not. -->\n @if (commandsCollapsed) {\n <kendo-dropdownbutton class=\"mm-toolbar-commands\" [data]=\"toolbarCommands\" textField=\"text\" [svgIcon]=\"commandsIcon\"\n fillMode=\"flat\" themeColor=\"primary\" [disabled]=\"isLoading()\"\n [title]=\"_messages.commands\"\n (itemClick)=\"onCommand($any($event).id)\"></kendo-dropdownbutton>\n } @else {\n @for (command of toolbarCommands; track command.id) {\n @switch (command.id) {\n @case ('excel') {\n <button kendoTooltip kendoGridExcelCommand themeColor=\"primary\" fillMode=\"flat\"\n [svgIcon]=\"command.svgIcon\" [disabled]=\"isLoading()\" [title]=\"command.text\"></button>\n }\n @case ('pdf') {\n <button kendoTooltip kendoGridPDFCommand themeColor=\"primary\" fillMode=\"flat\"\n [svgIcon]=\"command.svgIcon\" [disabled]=\"isLoading()\" [title]=\"command.text\"></button>\n }\n @default {\n <button kendoTooltip kendoButton themeColor=\"primary\" fillMode=\"flat\"\n [svgIcon]=\"command.svgIcon\" (click)=\"onCommand(command.id)\"\n [disabled]=\"isLoading()\" [title]=\"command.text\"></button>\n }\n }\n }\n }\n </ng-template>\n\n @if (showCheckboxColumn()) {\n <kendo-grid-checkbox-column [showSelectAll]=\"showRowSelectAllCheckBox\"\n [width]=\"effectiveCheckboxColumnWidth\"></kendo-grid-checkbox-column>\n }\n\n @if (!isCardMode) {\n @for (column of columns; track column.field) {\n <kendo-grid-column [field]=\"column.field\"\n [filter]=\"getFilterType(column)\" format=\"{{column.format}}\"\n [width]=\"$any(getEffectiveWidth(column))\"\n [minResizableWidth]=\"column.minWidth ?? 10\"\n [hidden]=\"isColumnHidden(column)\"\n [sortable]=\"column.sortable !== false\"\n [filterable]=\"column.filterable !== false\"\n [class]=\"alignClass(column)\"\n [headerClass]=\"alignClass(column)\"\n [title]=\"getDisplayName(column) | pascalCase\">\n <ng-template kendoGridFilterCellTemplate let-filter let-gridColumn=\"column\">\n @if (hasFilterOptions(column)) {\n <kendo-dropdownlist\n class=\"status-filter-dropdown\"\n [data]=\"column.filterOptions!\"\n textField=\"text\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n [value]=\"getSelectedFilterValue(column)\"\n [defaultItem]=\"{ text: '', value: null }\"\n [popupSettings]=\"{ width: 'auto' }\"\n (valueChange)=\"onDropdownFilter($event, column)\"\n >\n <ng-template kendoDropDownListValueTemplate let-dataItem>\n @if (dataItem?.value && column.statusMapping?.[dataItem.value]; as mapping) {\n <span class=\"filter-status-item\">\n <span [style.color]=\"mapping.color\">\n <kendo-svg-icon [icon]=\"mapping.icon\" [size]=\"'small'\"></kendo-svg-icon>\n </span>\n </span>\n } @else if (dataItem?.text) {\n <span>{{ dataItem.text }}</span>\n }\n </ng-template>\n <ng-template kendoDropDownListItemTemplate let-dataItem>\n @if (dataItem?.value && column.statusMapping?.[dataItem.value]; as mapping) {\n <span style=\"display: inline-flex; align-items: center; gap: 8px;\">\n <span [style.color]=\"mapping.color\">\n <kendo-svg-icon [icon]=\"mapping.icon\" [size]=\"'small'\"></kendo-svg-icon>\n </span>\n <span>{{ dataItem.text }}</span>\n </span>\n } @else {\n <span>{{ dataItem.text }}</span>\n }\n </ng-template>\n </kendo-dropdownlist>\n } @else if (column.dataType === 'numericRange') {\n <span class=\"numeric-range-filter\">\n <kendo-numerictextbox\n [value]=\"$any(getRangeFilterValue(column, 'gte'))\"\n (valueChange)=\"onRangeFilterChange($event, column, 'gte')\"\n [spinners]=\"false\"\n [decimals]=\"2\"\n placeholder=\"From\"\n size=\"small\"\n ></kendo-numerictextbox>\n <span class=\"range-separator\">\u2013</span>\n <kendo-numerictextbox\n [value]=\"$any(getRangeFilterValue(column, 'lte'))\"\n (valueChange)=\"onRangeFilterChange($event, column, 'lte')\"\n [spinners]=\"false\"\n [decimals]=\"2\"\n placeholder=\"To\"\n size=\"small\"\n ></kendo-numerictextbox>\n </span>\n } @else {\n @switch (getFilterType(column)) {\n @case ('numeric') {\n <kendo-grid-numeric-filter-cell [column]=\"gridColumn\" [filter]=\"filter\" [showOperators]=\"true\" [operator]=\"column.filterOperator || 'eq'\"\n [spinners]=\"!isCompactFilter(column)\" [class.mm-filter-compact]=\"isCompactFilter(column)\"></kendo-grid-numeric-filter-cell>\n }\n @case ('boolean') {\n <kendo-grid-boolean-filter-cell [column]=\"gridColumn\" [filter]=\"filter\"></kendo-grid-boolean-filter-cell>\n }\n @case ('date') {\n <kendo-grid-date-filter-cell [column]=\"gridColumn\" [filter]=\"filter\" [showOperators]=\"true\" [operator]=\"column.filterOperator || 'eq'\"\n [class.mm-filter-compact]=\"isCompactFilter(column)\"></kendo-grid-date-filter-cell>\n }\n @default {\n <kendo-grid-string-filter-cell [column]=\"gridColumn\" [filter]=\"filter\" [showOperators]=\"false\"></kendo-grid-string-filter-cell>\n }\n }\n }\n </ng-template>\n <ng-template kendoGridCellTemplate let-dataItem>\n <span\n [class.mm-cell-truncate]=\"column.truncate\"\n [attr.title]=\"column.truncate ? getCellTitle(dataItem, column) : null\">\n <ng-container\n [ngTemplateOutlet]=\"cellContent\"\n [ngTemplateOutletContext]=\"{ element: dataItem, column: column }\"\n ></ng-container>\n </span>\n </ng-template>\n </kendo-grid-column>\n }\n\n @if (_actionMenuItems.length > 0 || (_contextMenuItems.length > 0 && contextMenuType == 'actionMenu')) {\n <kendo-grid-command-column [title]=\"_messages.actionsColumnTitle\" [width]=\"effectiveActionsColumnWidth\">\n <ng-template kendoGridCellTemplate let-dataItem>\n <ng-container\n [ngTemplateOutlet]=\"rowActions\"\n [ngTemplateOutletContext]=\"{ dataItem: dataItem }\"\n ></ng-container>\n </ng-template>\n </kendo-grid-command-column>\n }\n } @else {\n <!-- Card mode (AB#4930): one full-width column renders the whole row as a\n stacked card \u2014 title from the first column, labeled lines for the\n rest (empty values skipped), row actions in the card header. -->\n <kendo-grid-column [sortable]=\"false\" [filterable]=\"false\" title=\"\">\n <ng-template kendoGridCellTemplate let-dataItem>\n <div class=\"mm-card\">\n <div class=\"mm-card-header\">\n @if (cardTitleColumn; as titleColumn) {\n <span class=\"mm-card-title\">\n <ng-container\n [ngTemplateOutlet]=\"cellContent\"\n [ngTemplateOutletContext]=\"{ element: dataItem, column: titleColumn }\"\n ></ng-container>\n </span>\n }\n <span class=\"mm-card-actions\">\n <ng-container\n [ngTemplateOutlet]=\"rowActions\"\n [ngTemplateOutletContext]=\"{ dataItem: dataItem }\"\n ></ng-container>\n </span>\n </div>\n <div class=\"mm-card-fields\">\n @for (column of cardBodyColumns; track column.field) {\n @if (hasCardValue(dataItem, column)) {\n <div class=\"mm-card-field\">\n <span class=\"mm-card-label\">{{ getDisplayName(column) | pascalCase }}</span>\n <span class=\"mm-card-value\">\n <ng-container\n [ngTemplateOutlet]=\"cellContent\"\n [ngTemplateOutletContext]=\"{ element: dataItem, column: column }\"\n ></ng-container>\n </span>\n </div>\n }\n }\n </div>\n </div>\n </ng-template>\n </kendo-grid-column>\n }\n\n <kendo-grid-excel fileName=\"{{excelExportFileName}}\">\n @for (column of columns; track column.field) {\n <kendo-excelexport-column [field]=\"column.field\"\n [title]=\"getDisplayName(column) | pascalCase\">\n </kendo-excelexport-column>\n }\n </kendo-grid-excel>\n\n <kendo-grid-pdf fileName=\"{{pdfExportFileName}}\" paperSize=\"A4\" [repeatHeaders]=\"true\"\n [landscape]=\"true\">\n @for (column of columns; track column.field) {\n <kendo-grid-column [field]=\"column.field\"\n [filter]=\"getFilterType(column)\"\n [title]=\"getDisplayName(column) | pascalCase\"></kendo-grid-column>\n }\n <kendo-grid-pdf-margin top=\"1.5cm\" left=\"1cm\" right=\"1cm\" bottom=\"1.5cm\"></kendo-grid-pdf-margin>\n <ng-template kendoGridPDFTemplate let-pageNum=\"pageNum\" let-totalPages=\"totalPages\">\n <div class=\"page-template\">\n <div class=\"footer\" style=\"position: absolute; bottom: 0; width: 100%; text-align: center; font-size: 9px; color: #666;\">\n {{ getPdfPageText(pageNum, totalPages) }}\n </div>\n </div>\n </ng-template>\n </kendo-grid-pdf>\n</kendo-grid>\n\n<kendo-contextmenu\n #gridmenu\n [kendoMenuHierarchyBinding]=\"_contextMenuItems\"\n [textField]=\"['text']\"\n childrenField=\"items\"\n svgIconField=\"svgIcon\"\n separatorField=\"separator\"\n disabledField=\"disabled\"\n (popupClose)=\"onContextMenuClosed($event)\"\n (select)=\"onContextMenuSelect($event)\"\n></kendo-contextmenu>\n", styles: [":host{display:flex;flex-direction:column;min-height:0}:host>kendo-grid{flex:1;min-height:0}.mm-search-input{width:165px}:host ::ng-deep .k-grid th:has(input[type=checkbox]),:host ::ng-deep .k-grid td:has(>kendo-checkbox){text-overflow:clip}:host ::ng-deep .k-command-cell{white-space:nowrap;overflow:hidden;vertical-align:middle}:host ::ng-deep .k-command-cell .k-button{display:inline-flex;vertical-align:middle;margin:0}:host ::ng-deep .k-command-cell .k-button+.k-button{margin-left:4px}:host(.mm-list-view-narrow) ::ng-deep .k-grid-toolbar{flex-wrap:wrap;row-gap:6px}:host(.mm-list-view-narrow) ::ng-deep .k-grid-toolbar .mm-search-input{width:100%;order:10}:host(.mm-list-view-narrow) ::ng-deep .k-grid-toolbar kendo-grid-spacer{display:none}:host(.mm-list-view-narrow) ::ng-deep .k-grid-toolbar .mm-toolbar-commands{margin-left:auto}:host ::ng-deep .k-grid td.mm-col-align-right{text-align:right;font-variant-numeric:tabular-nums}:host ::ng-deep .k-grid td.mm-col-align-center{text-align:center}:host ::ng-deep .k-grid th.mm-col-align-right .k-cell-inner>.k-link{justify-content:flex-end}:host ::ng-deep .k-grid th.mm-col-align-center .k-cell-inner>.k-link{justify-content:center}:host ::ng-deep .k-filter-row .mm-filter-compact .k-filtercell-wrapper{gap:2px}:host ::ng-deep .k-filter-row .mm-filter-compact .k-datepicker>.k-input-button{display:none}:host ::ng-deep .k-filter-row .mm-filter-compact .k-input-inner{padding-left:4px;padding-right:4px;font-size:12px}:host ::ng-deep .k-filter-row .mm-filter-compact .k-filtercell-operator .k-dropdown-operator{width:26px;min-width:0}:host ::ng-deep .k-filter-row .mm-filter-compact .k-filtercell-operator .k-dropdown-operator .k-input-button{padding:2px}.mm-cell-truncate{display:block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.status-icons-cell{display:inline-flex;align-items:center;gap:8px}.status-icons-cell .status-icon{display:inline-flex;align-items:center;justify-content:center;cursor:default}.status-icons-cell .status-icon kendo-svg-icon{width:18px;height:18px}:host ::ng-deep .filter-status-item{display:inline-flex;align-items:center;gap:10px}:host ::ng-deep .status-filter-dropdown .k-input-value-text{font-size:0}:host ::ng-deep .status-filter-dropdown .k-input-value-text .filter-status-item,:host ::ng-deep .status-filter-dropdown .k-input-value-text>span{font-size:14px}.numeric-range-filter{display:flex;align-items:center;gap:4px}.numeric-range-filter kendo-numerictextbox{flex:1;min-width:0}.numeric-range-filter .range-separator{flex-shrink:0;opacity:.6}.cron-expression-cell{display:flex;flex-direction:column;gap:2px}.cron-expression-cell .cron-expression{font-family:Roboto Mono,Consolas,monospace;font-size:.85em;background:var(--mm-cron-code-bg, #eeeeee);color:var(--mm-cron-code-text, #333333);padding:2px 6px;border-radius:3px}.cron-expression-cell .cron-description{font-size:.8em;color:var(--mm-cron-text-secondary, #666666)}.badge-cell{display:inline-block;padding:2px 10px;font-size:.75rem;font-weight:600;letter-spacing:.5px;text-transform:uppercase;border-radius:12px;border:1px solid var(--mm-badge-default-border, transparent);background:var(--mm-badge-default-bg, rgba(127, 127, 127, .15));color:var(--mm-badge-default-color, inherit);white-space:nowrap;line-height:1.4}.badge-cell-default{opacity:.7;font-style:italic}.progress-bar-cell{display:inline-flex;align-items:center;gap:8px;width:100%}.progress-bar-cell .progress-bar-track{flex:1;height:8px;background:var(--mm-progress-track-bg, rgba(255, 255, 255, .1));border-radius:4px;overflow:hidden}.progress-bar-cell .progress-bar-fill{display:block;height:100%;background:var(--mm-progress-fill-color, #10b981);border-radius:4px;transition:width .3s ease}.progress-bar-cell .progress-bar-label{min-width:40px;text-align:right;font-size:.85em}:host-context(.k-pdf-export) .k-grid,:host-context(.k-pdf-export) .k-grid-header,:host-context(.k-pdf-export) .k-grid-content,:host-context(.k-pdf-export) .k-grid-header-wrap,:host-context(.k-pdf-export) .k-grid-content-locked,:host-context(.k-pdf-export) .k-grid-header-locked{background:#fff!important;background-color:#fff!important;background-image:none!important;color:#000!important;border-color:#000!important}:host-context(.k-pdf-export) .k-grid{border:1px solid #000!important;font-family:Arial,sans-serif!important;font-size:10px!important}:host-context(.k-pdf-export) .k-grid-header th,:host-context(.k-pdf-export) .k-header,:host-context(.k-pdf-export) .k-grid th{background:#f0f0f0!important;background-color:#f0f0f0!important;background-image:none!important;color:#000!important;border:1px solid #000!important;font-weight:700!important;padding:6px 8px!important;text-transform:none!important;letter-spacing:normal!important}:host-context(.k-pdf-export) .k-grid td,:host-context(.k-pdf-export) .k-grid-content td,:host-context(.k-pdf-export) .k-master-row td{background:#fff!important;background-color:#fff!important;background-image:none!important;color:#000!important;border:1px solid #000!important;padding:4px 8px!important}:host-context(.k-pdf-export) .k-grid tr,:host-context(.k-pdf-export) .k-master-row,:host-context(.k-pdf-export) .k-alt{background:#fff!important;background-color:#fff!important;background-image:none!important}:host-context(.k-pdf-export) .k-grid tr:hover,:host-context(.k-pdf-export) .k-grid tr.k-selected,:host-context(.k-pdf-export) .k-grid td:hover{background:#fff!important;background-color:#fff!important}:host-context(.k-pdf-export) .k-grid-toolbar,:host-context(.k-pdf-export) .k-pager,:host-context(.k-pdf-export) .k-grid-pager,:host-context(.k-pdf-export) .k-command-cell,:host-context(.k-pdf-export) .k-checkbox-column,:host-context(.k-pdf-export) kendo-grid-checkbox-column,:host-context(.k-pdf-export) .status-icons-cell,:host-context(.k-pdf-export) .status-icon,:host-context(.k-pdf-export) kendo-svg-icon{display:none!important}:host-context(.k-pdf-export) .badge-cell{background:transparent!important;border:none!important;color:inherit!important;padding:0!important;border-radius:0!important;text-transform:none!important;font-weight:inherit!important;letter-spacing:0!important;font-size:inherit!important}:host(.mm-list-view-cards) ::ng-deep .k-grid-header{display:none}:host(.mm-list-view-cards) ::ng-deep .k-grid td,:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-td{border:none;background:transparent;padding:4px 2px;white-space:normal}:host(.mm-list-view-cards) ::ng-deep .k-grid td:has(>kendo-checkbox),:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-td:has(>kendo-checkbox){padding-left:2px;padding-right:0}:host(.mm-list-view-cards) ::ng-deep .k-grid colgroup>col:first-child:not(:only-child){width:32px!important}:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-row:hover,:host(.mm-list-view-cards) ::ng-deep .k-grid tr:hover{background:transparent}:host(.mm-list-view-cards) ::ng-deep .k-grid tr.k-selected .mm-card,:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-row.k-selected .mm-card{border-color:var(--kendo-color-primary, #ff6358)}:host(.mm-list-view-cards) ::ng-deep .k-grid tr.k-selected>td,:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-row.k-selected>.k-table-td{background:transparent}.mm-card{display:flex;flex-direction:column;gap:6px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:8px;padding:10px 12px}.mm-card-header{display:flex;align-items:center;gap:8px}.mm-card-title{flex:1;min-width:0;font-weight:600;overflow-wrap:anywhere}.mm-card-actions{display:flex;align-items:center;gap:2px;flex-shrink:0}.mm-card-fields{display:flex;flex-direction:column;gap:3px}.mm-card-field{display:flex;align-items:baseline;gap:8px;font-size:.9em}.mm-card-label{flex:0 0 38%;max-width:160px;color:var(--kendo-color-subtle, #666666);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mm-card-value{flex:1;min-width:0;overflow-wrap:anywhere}::ng-deep .k-grid-toolbar .mm-toolbar-filters{display:flex;align-items:center;gap:12px;flex-wrap:wrap}::ng-deep .k-grid-toolbar .mm-toolbar-filters-separator{align-self:center;height:22px;margin:0 4px;border-left:1px solid var(--theme-border-subtle, rgba(0, 0, 0, .16));border-top:none;width:0}\n"], dependencies: [{ kind: "component", type: GridComponent, selector: "kendo-grid", inputs: ["data", "pageSize", "height", "rowHeight", "adaptiveMode", "detailRowHeight", "skip", "scrollable", "selectable", "sort", "size", "trackBy", "filter", "group", "virtualColumns", "filterable", "sortable", "pageable", "groupable", "gridResizable", "rowReorderable", "navigable", "autoSize", "rowClass", "rowSticky", "rowSelected", "isRowSelectable", "cellSelected", "resizable", "reorderable", "loading", "columnMenu", "hideHeader", "showInactiveTools", "isDetailExpanded", "isGroupExpanded", "dataLayoutMode", "pinnable", "pinnedTopRows", "pinnedBottomRows", "isRowPinnable"], outputs: ["filterChange", "pageChange", "groupChange", "sortChange", "selectionChange", "rowReorder", "rowPinChange", "dataStateChange", "gridStateChange", "groupExpand", "groupCollapse", "detailExpand", "detailCollapse", "edit", "cancel", "save", "remove", "add", "cellClose", "cellClick", "pdfExport", "excelExport", "csvExport", "columnResize", "columnReorder", "columnVisibilityChange", "columnLockedChange", "columnStickyChange", "scrollBottom", "contentScroll"], exportAs: ["kendoGrid"] }, { kind: "directive", type: MmListViewDataBindingDirective, selector: "[mmListViewDataBinding]" }, { kind: "component", type: ColumnComponent, selector: "kendo-grid-column", inputs: ["field", "format", "sortable", "groupable", "editor", "filter", "filterVariant", "filterable", "editable"] }, { kind: "directive", type: ToolbarTemplateDirective, selector: "[kendoGridToolbarTemplate]", inputs: ["position"] }, { kind: "component", type: GridSpacerComponent, selector: "kendo-grid-spacer", inputs: ["width"] }, { kind: "ngmodule", type: ExcelModule }, { kind: "component", type: i1$1.ExcelComponent, selector: "kendo-grid-excel", inputs: ["fileName", "filterable", "creator", "date", "forceProxy", "proxyURL", "fetchData", "paddingCellOptions", "headerPaddingCellOptions", "collapsible"], outputs: ["fileCreated"] }, { kind: "component", type: i1$1.ExcelCommandDirective, selector: "[kendoGridExcelCommand]" }, { kind: "component", type: i2.ColumnComponent, selector: "kendo-excelexport-column", inputs: ["field", "cellOptions", "groupHeaderCellOptions", "groupFooterCellOptions", "footerCellOptions"] }, { kind: "ngmodule", type: PDFModule }, { kind: "component", type: i1$1.PDFComponent, selector: "kendo-grid-pdf", inputs: ["allPages", "delay"] }, { kind: "component", type: i1$1.PDFMarginComponent, selector: "kendo-grid-pdf-margin" }, { kind: "component", type: i1$1.PDFCommandDirective, selector: "[kendoGridPDFCommand]" }, { kind: "directive", type: i1$1.PDFTemplateDirective, selector: "[kendoGridPDFTemplate]" }, { kind: "component", type: ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "component", type: DropDownButtonComponent, selector: "kendo-dropdownbutton", inputs: ["arrowIcon", "icon", "svgIcon", "iconClass", "imageUrl", "textField", "data", "size", "rounded", "fillMode", "themeColor", "buttonAttributes"], outputs: ["itemClick", "focus", "blur"], exportAs: ["kendoDropDownButton"] }, { kind: "component", type: SplitButtonComponent, selector: "kendo-splitbutton", inputs: ["text", "icon", "svgIcon", "iconClass", "type", "imageUrl", "size", "rounded", "fillMode", "themeColor", "disabled", "popupSettings", "tabIndex", "textField", "data", "arrowButtonClass", "arrowButtonIcon", "arrowButtonSvgIcon", "buttonAttributes"], outputs: ["buttonClick", "itemClick", "focus", "blur", "open", "close"], exportAs: ["kendoSplitButton"] }, { kind: "component", type: CommandColumnComponent, selector: "kendo-grid-command-column" }, { kind: "directive", type: CellTemplateDirective, selector: "[kendoGridCellTemplate]" }, { kind: "component", type: ContextMenuComponent, selector: "kendo-contextmenu", inputs: ["showOn", "target", "filter", "alignToAnchor", "vertical", "popupAnimate", "popupAlign", "anchorAlign", "collision", "appendTo", "ariaLabel"], outputs: ["popupOpen", "popupClose", "select", "open", "close"], exportAs: ["kendoContextMenu"] }, { kind: "directive", type: HierarchyBindingDirective, selector: "[kendoMenuHierarchyBinding]", inputs: ["kendoMenuHierarchyBinding", "textField", "urlField", "iconField", "svgIconField", "disabledField", "cssClassField", "cssStyleField", "separatorField", "childrenField"], exportAs: ["kendoMenuHierarchyBinding"] }, { kind: "component", type: CheckboxColumnComponent, selector: "kendo-grid-checkbox-column", inputs: ["showSelectAll", "showDisabledCheckbox"] }, { kind: "component", type: CheckBoxComponent, selector: "kendo-checkbox", inputs: ["checkedState", "rounded"], outputs: ["checkedStateChange"], exportAs: ["kendoCheckBox"] }, { kind: "component", type: SeparatorComponent, selector: "kendo-separator", inputs: ["orientation"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i3.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon", "variant"], exportAs: ["kendoSVGIcon"] }, { kind: "component", type: CustomMessagesComponent, selector: "kendo-grid-messages" }, { kind: "directive", type: FilterCellTemplateDirective, selector: "[kendoGridFilterCellTemplate]" }, { kind: "component", type: StringFilterCellComponent, selector: "kendo-grid-string-filter-cell", inputs: ["filterDelay", "showOperators", "placeholder"] }, { kind: "component", type: NumericFilterCellComponent, selector: "kendo-grid-numeric-filter-cell", inputs: ["filterDelay", "showOperators", "placeholder"] }, { kind: "component", type: BooleanFilterCellComponent, selector: "kendo-grid-boolean-filter-cell" }, { kind: "component", type: DateFilterCellComponent, selector: "kendo-grid-date-filter-cell", inputs: ["showOperators"] }, { kind: "component", type: DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "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: "directive", type: ValueTemplateDirective, selector: "[kendoDropDownListValueTemplate],[kendoDropDownTreeValueTemplate]" }, { kind: "directive", type: ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: NumericTextBoxComponent, selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: PascalCasePipe, name: "pascalCase" }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "pipe", type: DecimalPipe, name: "number" }, { kind: "pipe", type: BytesToSizePipe, name: "bytesToSize" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4015
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: ListViewComponent, isStandalone: true, selector: "mm-list-view", inputs: { pageSize: "pageSize", skip: "skip", autoPageSize: "autoPageSize", rowIsClickable: "rowIsClickable", showRowCheckBoxes: "showRowCheckBoxes", showRowSelectAllCheckBox: "showRowSelectAllCheckBox", contextMenuType: "contextMenuType", leftToolbarActions: "leftToolbarActions", rightToolbarActions: "rightToolbarActions", actionCommandItems: "actionCommandItems", contextMenuCommandItems: "contextMenuCommandItems", rowActions: "rowActions", maxInlineRowActions: "maxInlineRowActions", excelExportFileName: "excelExportFileName", pdfExportFileName: "pdfExportFileName", pageable: "pageable", sortable: "sortable", rowFilterEnabled: "rowFilterEnabled", searchTextBoxEnabled: "searchTextBoxEnabled", persistListState: "persistListState", listStateKey: "listStateKey", resizable: "resizable", actionsColumnWidth: "actionsColumnWidth", hideCheckboxesBelow: "hideCheckboxesBelow", cardModeBelow: "cardModeBelow", collapseCommandsBelow: "collapseCommandsBelow", hasExternalFilters: "hasExternalFilters", emptyState: "emptyState", emptyTemplate: "emptyTemplate", rowClass: "rowClass", messages: "messages", selectable: "selectable", columns: "columns", rowLabelField: "rowLabelField" }, outputs: { rowClicked: "rowClicked", rowAction: "rowAction", resetFilters: "resetFilters" }, host: { properties: { "class.mm-list-view-narrow": "this.isNarrow", "class.mm-list-view-cards": "this.isCardMode" } }, queries: [{ propertyName: "filtersTemplate", first: true, predicate: ListViewFiltersDirective, descendants: true }], viewQueries: [{ propertyName: "gridComponent", first: true, predicate: GridComponent, descendants: true }, { propertyName: "dataBindingDirective", first: true, predicate: MmListViewDataBindingDirective, descendants: true }, { propertyName: "gridContextMenu", first: true, predicate: ["gridmenu"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<!-- Shared cell renderer (AB#4930): used by the regular grid columns AND the\n card-mode body lines, so both render every dataType identically. -->\n<ng-template #cellContent let-element=\"element\" let-column=\"column\">\n @if (column.formatter) {\n {{ getFormattedValue(element, column) }}\n } @else {\n @switch (column.dataType) {\n @case ('boolean') {\n <!-- Read-only marker: named after its column so a screen reader says what is checked (AB#5621). -->\n <kendo-checkbox type=\"checkbox\" [checkedState]=\"$any(getValue(element, column))\" [disabled]=\"true\"\n [inputAttributes]=\"booleanCellAttributes(column)\" />\n }\n @case ('booleanIcon') {\n <!-- Boolean as icon (AB#5623): named \"<column>: Yes|No\" like the status icons (G13). -->\n @if (booleanIconState(element, column); as state) {\n @if (booleanIcon(column, state); as icon) {\n <span\n class=\"boolean-icon\"\n [class.boolean-icon--true]=\"state === 'true'\"\n [class.boolean-icon--false]=\"state === 'false'\"\n role=\"img\"\n [title]=\"booleanIconLabel(column, state)\"\n [attr.aria-label]=\"booleanIconLabel(column, state)\"\n [style.color]=\"state === 'true' ? column.booleanIcons?.trueColor : column.booleanIcons?.falseColor\">\n <kendo-svg-icon [icon]=\"icon\"></kendo-svg-icon>\n </span>\n }\n }\n }\n @case ('iso8601') {\n {{ $any(getValue(element, column)) | date:column.format }}\n }\n @case ('bytes') {\n {{ $any(getValue(element, column)) | bytesToSize }}\n }\n @case ('statusIcons') {\n <span class=\"status-icons-cell\">\n @for (fieldConfig of getStatusFields(column); track fieldConfig.field) {\n @if (getStatusIconMapping(element, fieldConfig); as mapping) {\n <!-- Icon-only status: the mapping's tooltip is also its accessible name (AB#5621). -->\n <span\n class=\"status-icon\"\n role=\"img\"\n [title]=\"mapping.tooltip\"\n [attr.aria-label]=\"mapping.tooltip || null\"\n [style.color]=\"mapping.color\">\n <kendo-svg-icon [icon]=\"mapping.icon\"></kendo-svg-icon>\n </span>\n }\n }\n </span>\n }\n @case ('cronExpression') {\n <span class=\"cron-expression-cell\">\n <code class=\"cron-expression\">{{ getValue(element, column) }}</code>\n <span class=\"cron-description\">{{ getCronHumanReadable(getValue(element, column)) }}</span>\n </span>\n }\n @case ('numeric') {\n {{ $any(getValue(element, column)) | number:column.format }}\n }\n @case ('progressBar') {\n <span class=\"progress-bar-cell\">\n <span class=\"progress-bar-track\">\n <span class=\"progress-bar-fill\" [style.width.%]=\"getValue(element, column)\"></span>\n </span>\n <span class=\"progress-bar-label\">{{ getValue(element, column) }}%</span>\n </span>\n }\n @case ('badge') {\n @if (getBadgeMapping(element, column); as badge) {\n <span\n class=\"badge-cell\"\n [title]=\"badge.tooltip ?? ''\"\n [style.color]=\"badge.color\"\n [style.background]=\"badge.backgroundColor\"\n [style.borderColor]=\"badge.borderColor ?? badge.backgroundColor\">\n {{ badge.label ?? getValue(element, column) }}\n </span>\n } @else if (!column.badgeHideUnmapped) {\n <span class=\"badge-cell badge-cell-default\">{{ getValue(element, column) }}</span>\n }\n }\n @case ('component') {\n @if (column.cellComponent) {\n <ng-container *ngComponentOutlet=\"column.cellComponent; inputs: getCellInputs(element, column)\" />\n }\n }\n @default {\n {{ getValue(element, column) }}\n }\n }\n }\n</ng-template>\n\n<!-- Row actions (AB#4930, AB#5572): shared by the actions column and the card header. One MmAction\n per action (CommandItems via adapter): neutral icon buttons, danger colour only for destructive\n actions, max `maxInlineRowActions` slots, the rest (and the context menu) behind \"\u2026\". Disabled\n actions stay focusable and announce their reason (mm-action-button). -->\n<ng-template #rowActions let-dataItem=\"dataItem\">\n @let view = rowActionsView(dataItem);\n @if (view.inline.length > 0 || view.hasMenu) {\n <span class=\"mm-list-view-row-actions\" role=\"group\" [attr.aria-label]=\"rowActionsGroupLabel(dataItem)\">\n @for (entry of view.inline; track entry.action.id) {\n <!-- Clicks never reach the grid (no row selection / row navigation from an action). -->\n <mm-action-button [action]=\"entry.action\" [targetLabel]=\"rowLabel(dataItem)\" context=\"row\" display=\"icon\"\n (click)=\"$event.stopPropagation()\"\n (triggered)=\"onInlineRowAction(entry, dataItem)\" />\n }\n @if (view.hasMenu) {\n <button kendoButton themeColor=\"base\" fillMode=\"flat\" size=\"small\" class=\"mm-list-view-row-actions__more\"\n [svgIcon]=\"moreVerticalIcon\" data-action=\"more\"\n [title]=\"rowActionsLabel(dataItem)\" [attr.aria-label]=\"rowActionsLabel(dataItem)\" aria-haspopup=\"menu\"\n [disabled]=\"isLoading()\" (click)=\"$event.stopPropagation(); onContextMenu(dataItem, $event)\"></button>\n }\n </span>\n }\n</ng-template>\n\n<kendo-grid\n mmListViewDataBinding\n [loading]=\"isLoading()\"\n [pageSize]=\"effectivePageSize()\" [selectable]=\"selectable\" (pageChange)=\"onPageChange($event)\"\n [skip]=\"skip\" (selectionChange)=\"onRowSelect($event)\" (cellClick)=\"onCellClick($event)\"\n [pageable]=\"effectivePageable\"\n [sortable]=\"sortable\"\n [filterable]=\"_showRowFilter && !isCardMode\"\n [resizable]=\"resizable\"\n [rowClass]=\"rowClass ?? defaultRowClass\"\n>\n <kendo-grid-messages\n [pagerItemsPerPage]=\"_messages.pagerItemsPerPage\"\n [pagerOf]=\"_messages.pagerOf\"\n [pagerItems]=\"_messages.pagerItems\"\n [pagerPage]=\"_messages.pagerPage\"\n [pagerFirstPage]=\"_messages.pagerFirstPage\"\n [pagerLastPage]=\"_messages.pagerLastPage\"\n [pagerPreviousPage]=\"_messages.pagerPreviousPage\"\n [pagerNextPage]=\"_messages.pagerNextPage\"\n [noRecords]=\"_messages.noRecords\"\n ></kendo-grid-messages>\n <!-- Empty state (AB#3444): one block per list, never per row. -->\n <ng-template kendoGridNoRecordsTemplate>\n @if (emptyTemplate) {\n <ng-container *ngTemplateOutlet=\"emptyTemplate; context: emptyContext\"></ng-container>\n } @else if (emptyState) {\n @if (loadSettled() && !isLoading()) {\n @if (isFiltered) {\n <mm-empty-state class=\"mm-list-view-empty\" variant=\"no-results\"\n [heading]=\"emptyState.filteredTitle || _messages.noResultsTitle\"\n [text]=\"emptyState.filteredText || _messages.noResultsText\"></mm-empty-state>\n } @else {\n <mm-empty-state class=\"mm-list-view-empty\" variant=\"empty\"\n [heading]=\"emptyState.title\" [text]=\"emptyState.text\"></mm-empty-state>\n }\n }\n } @else {\n {{ _messages.noRecords }}\n }\n </ng-template>\n <ng-template kendoGridToolbarTemplate>\n\n @for (commandItem of leftToolbarActions; track commandItem.id) {\n @if (commandItem) {\n @switch (commandItem.type) {\n @case ('link') {\n @if (commandItem.children && commandItem.children.length > 0 && commandItem.onClick) {\n <kendo-splitbutton\n [data]=\"commandItem.children\"\n [svgIcon]=\"$any(commandItem.svgIcon)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n [textField]=\"'text'\"\n themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [buttonAttributes]=\"toolbarItemButtonAttributes(commandItem)\"\n (buttonClick)=\"onToolbarCommand(commandItem)\"\n (itemClick)=\"onToolbarDropdownItemClick($event)\">\n {{ commandItem.text }}\n </kendo-splitbutton>\n } @else if (commandItem.children && commandItem.children.length > 0) {\n <kendo-dropdownbutton\n [data]=\"commandItem.children\"\n [svgIcon]=\"$any(commandItem.svgIcon)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n [textField]=\"'text'\"\n themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [buttonAttributes]=\"toolbarItemButtonAttributes(commandItem)\"\n (itemClick)=\"onToolbarDropdownItemClick($event)\">\n {{ commandItem.text }}\n </kendo-dropdownbutton>\n } @else if (commandItem.svgIcon) {\n <button kendoTooltip kendoButton themeColor=\"primary\" [svgIcon]=\"commandItem.svgIcon\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [attr.aria-label]=\"toolbarItemAriaLabel(commandItem)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n (click)=\"onToolbarCommand(commandItem)\">{{ commandItem.text }}\n </button>\n } @else {\n <button kendoTooltip kendoButton themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [attr.aria-label]=\"toolbarItemAriaLabel(commandItem)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n (click)=\"onToolbarCommand(commandItem)\">{{ commandItem.text }}\n </button>\n }\n }\n @case ('separator'){\n <kendo-separator></kendo-separator>\n }\n }\n }\n }\n\n <!-- Host scope/filter controls (view switches and the like). They steer the\n table, so they sit in the table's own band instead of a second strip\n stacked above the grid. -->\n @if (filtersTemplate) {\n <!-- A rule between the host's actions and its filters: both sit on the\n left of the same band, but one acts on records and the other changes\n what the list shows. Only drawn when actions precede them. -->\n @if (leftToolbarActions.length > 0) {\n <kendo-separator class=\"mm-toolbar-filters-separator\"></kendo-separator>\n }\n <div class=\"mm-toolbar-filters\">\n <ng-container [ngTemplateOutlet]=\"filtersTemplate.templateRef\"></ng-container>\n </div>\n }\n\n <kendo-grid-spacer></kendo-grid-spacer>\n @if (searchTextBoxEnabled) {\n <input\n class=\"k-textbox k-input k-input-md k-rounded-md mm-search-input\"\n [placeholder]=\"_messages.searchPlaceholder\"\n [value]=\"searchValue\"\n (input)=\"onFilter($any($event.target).value || null)\"\n />\n }\n\n @for (commandItem of rightToolbarActions; track commandItem.id) {\n @if (commandItem) {\n @switch (commandItem.type) {\n @case ('link') {\n @if (commandItem.children && commandItem.children.length > 0 && commandItem.onClick) {\n <kendo-splitbutton\n [data]=\"commandItem.children\"\n [svgIcon]=\"$any(commandItem.svgIcon)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n [textField]=\"'text'\"\n themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [buttonAttributes]=\"toolbarItemButtonAttributes(commandItem)\"\n (buttonClick)=\"onToolbarCommand(commandItem)\"\n (itemClick)=\"onToolbarDropdownItemClick($event)\">\n {{ commandItem.text }}\n </kendo-splitbutton>\n } @else if (commandItem.children && commandItem.children.length > 0) {\n <kendo-dropdownbutton\n [data]=\"commandItem.children\"\n [svgIcon]=\"$any(commandItem.svgIcon)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n [textField]=\"'text'\"\n themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [buttonAttributes]=\"toolbarItemButtonAttributes(commandItem)\"\n (itemClick)=\"onToolbarDropdownItemClick($event)\">\n {{ commandItem.text }}\n </kendo-dropdownbutton>\n } @else if (commandItem.svgIcon) {\n <button kendoTooltip kendoButton themeColor=\"primary\" [svgIcon]=\"commandItem.svgIcon\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [attr.aria-label]=\"toolbarItemAriaLabel(commandItem)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n (click)=\"onToolbarCommand(commandItem)\">{{ commandItem.text }}\n </button>\n } @else {\n <button kendoTooltip kendoButton themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [attr.aria-label]=\"toolbarItemAriaLabel(commandItem)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n (click)=\"onToolbarCommand(commandItem)\">{{ commandItem.text }}\n </button>\n }\n }\n @case ('separator'){\n <kendo-separator></kendo-separator>\n }\n }\n }\n }\n\n <!-- Commands: laid out while there is room, collapsed into one menu once\n the list gets narrow. Same behaviour in every host app \u2014 a list this\n wide has space for five buttons, and a list this narrow does not. -->\n @if (commandsCollapsed) {\n <kendo-dropdownbutton class=\"mm-toolbar-commands\" [data]=\"toolbarCommands\" textField=\"text\" [svgIcon]=\"commandsIcon\"\n fillMode=\"flat\" themeColor=\"primary\" [disabled]=\"isLoading()\"\n [title]=\"_messages.commands\"\n [buttonAttributes]=\"commandsMenuButtonAttributes\"\n (itemClick)=\"onCommand($any($event).id)\"></kendo-dropdownbutton>\n } @else {\n @for (command of toolbarCommands; track command.id) {\n @switch (command.id) {\n @case ('excel') {\n <button kendoTooltip kendoGridExcelCommand themeColor=\"primary\" fillMode=\"flat\"\n [svgIcon]=\"command.svgIcon\" [disabled]=\"isLoading()\" [title]=\"command.text\"\n [attr.aria-label]=\"command.text\" [attr.data-command]=\"command.id\"></button>\n }\n @case ('pdf') {\n <button kendoTooltip kendoGridPDFCommand themeColor=\"primary\" fillMode=\"flat\"\n [svgIcon]=\"command.svgIcon\" [disabled]=\"isLoading()\" [title]=\"command.text\"\n [attr.aria-label]=\"command.text\" [attr.data-command]=\"command.id\"></button>\n }\n @default {\n <!-- The row filter is a toggle: its on state is shown (k-selected) and announced. -->\n <button kendoTooltip kendoButton themeColor=\"primary\" fillMode=\"flat\"\n [svgIcon]=\"command.svgIcon\" (click)=\"onCommand(command.id)\"\n [class.k-selected]=\"command.id === 'rowFilter' && _showRowFilter\"\n [attr.aria-pressed]=\"command.id === 'rowFilter' ? _showRowFilter : null\"\n [attr.data-command]=\"command.id\"\n [disabled]=\"isLoading()\" [title]=\"command.text\" [attr.aria-label]=\"command.text\"></button>\n }\n }\n }\n }\n </ng-template>\n\n @if (showCheckboxColumn()) {\n <kendo-grid-checkbox-column [showSelectAll]=\"showRowSelectAllCheckBox\"\n [width]=\"effectiveCheckboxColumnWidth\"></kendo-grid-checkbox-column>\n }\n\n @if (!isCardMode) {\n @for (column of columns; track column.field) {\n <kendo-grid-column [field]=\"column.field\"\n [filter]=\"getFilterType(column)\" format=\"{{column.format}}\"\n [width]=\"$any(getEffectiveWidth(column))\"\n [minResizableWidth]=\"column.minWidth ?? 10\"\n [hidden]=\"isColumnHidden(column)\"\n [sortable]=\"column.sortable !== false\"\n [filterable]=\"column.filterable !== false\"\n [class]=\"alignClass(column)\"\n [headerClass]=\"alignClass(column)\"\n [title]=\"getDisplayName(column) | pascalCase\">\n <ng-template kendoGridFilterCellTemplate let-filter let-gridColumn=\"column\">\n @if (hasFilterOptions(column)) {\n <kendo-dropdownlist\n class=\"status-filter-dropdown\"\n [data]=\"column.filterOptions!\"\n textField=\"text\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n [value]=\"getSelectedFilterValue(column)\"\n [defaultItem]=\"{ text: '', value: null }\"\n [popupSettings]=\"{ width: 'auto' }\"\n (valueChange)=\"onDropdownFilter($event, column)\"\n >\n <ng-template kendoDropDownListValueTemplate let-dataItem>\n @if (dataItem?.value && column.statusMapping?.[dataItem.value]; as mapping) {\n <span class=\"filter-status-item\">\n <span role=\"img\" [attr.aria-label]=\"dataItem.text || mapping.tooltip || null\" [title]=\"dataItem.text || mapping.tooltip\"\n [style.color]=\"mapping.color\">\n <kendo-svg-icon [icon]=\"mapping.icon\" [size]=\"'small'\"></kendo-svg-icon>\n </span>\n </span>\n } @else if (dataItem?.text) {\n <span>{{ dataItem.text }}</span>\n }\n </ng-template>\n <ng-template kendoDropDownListItemTemplate let-dataItem>\n @if (dataItem?.value && column.statusMapping?.[dataItem.value]; as mapping) {\n <span style=\"display: inline-flex; align-items: center; gap: 8px;\">\n <span [style.color]=\"mapping.color\">\n <kendo-svg-icon [icon]=\"mapping.icon\" [size]=\"'small'\"></kendo-svg-icon>\n </span>\n <span>{{ dataItem.text }}</span>\n </span>\n } @else {\n <span>{{ dataItem.text }}</span>\n }\n </ng-template>\n </kendo-dropdownlist>\n } @else if (column.dataType === 'numericRange') {\n <span class=\"numeric-range-filter\">\n <kendo-numerictextbox\n [value]=\"$any(getRangeFilterValue(column, 'gte'))\"\n (valueChange)=\"onRangeFilterChange($event, column, 'gte')\"\n [spinners]=\"false\"\n [decimals]=\"2\"\n placeholder=\"From\"\n size=\"small\"\n ></kendo-numerictextbox>\n <span class=\"range-separator\">\u2013</span>\n <kendo-numerictextbox\n [value]=\"$any(getRangeFilterValue(column, 'lte'))\"\n (valueChange)=\"onRangeFilterChange($event, column, 'lte')\"\n [spinners]=\"false\"\n [decimals]=\"2\"\n placeholder=\"To\"\n size=\"small\"\n ></kendo-numerictextbox>\n </span>\n } @else {\n @switch (getFilterType(column)) {\n @case ('numeric') {\n <kendo-grid-numeric-filter-cell [column]=\"gridColumn\" [filter]=\"filter\" [showOperators]=\"true\" [operator]=\"column.filterOperator || 'eq'\"\n [spinners]=\"!isCompactFilter(column)\" [class.mm-filter-compact]=\"isCompactFilter(column)\"></kendo-grid-numeric-filter-cell>\n }\n @case ('boolean') {\n <kendo-grid-boolean-filter-cell [column]=\"gridColumn\" [filter]=\"filter\"></kendo-grid-boolean-filter-cell>\n }\n @case ('date') {\n <kendo-grid-date-filter-cell [column]=\"gridColumn\" [filter]=\"filter\" [showOperators]=\"true\" [operator]=\"column.filterOperator || 'eq'\"\n [class.mm-filter-compact]=\"isCompactFilter(column)\"></kendo-grid-date-filter-cell>\n }\n @default {\n <kendo-grid-string-filter-cell [column]=\"gridColumn\" [filter]=\"filter\" [showOperators]=\"false\"></kendo-grid-string-filter-cell>\n }\n }\n }\n </ng-template>\n <ng-template kendoGridCellTemplate let-dataItem>\n <span\n [class.mm-cell-truncate]=\"column.truncate\"\n [attr.title]=\"column.truncate ? getCellTitle(dataItem, column) : null\">\n <ng-container\n [ngTemplateOutlet]=\"cellContent\"\n [ngTemplateOutletContext]=\"{ element: dataItem, column: column }\"\n ></ng-container>\n </span>\n </ng-template>\n </kendo-grid-column>\n }\n\n @if (hasRowActions) {\n <kendo-grid-command-column [title]=\"_messages.actionsColumnTitle\" [width]=\"effectiveActionsColumnWidth\">\n <ng-template kendoGridCellTemplate let-dataItem>\n <ng-container\n [ngTemplateOutlet]=\"rowActions\"\n [ngTemplateOutletContext]=\"{ dataItem: dataItem }\"\n ></ng-container>\n </ng-template>\n </kendo-grid-command-column>\n }\n } @else {\n <!-- Card mode (AB#4930): one full-width column renders the whole row as a\n stacked card \u2014 title from the first column, labeled lines for the\n rest (empty values skipped), row actions in the card header. -->\n <kendo-grid-column [sortable]=\"false\" [filterable]=\"false\" title=\"\">\n <ng-template kendoGridCellTemplate let-dataItem>\n <div class=\"mm-card\">\n <div class=\"mm-card-header\">\n @if (cardTitleColumn; as titleColumn) {\n <span class=\"mm-card-title\">\n <ng-container\n [ngTemplateOutlet]=\"cellContent\"\n [ngTemplateOutletContext]=\"{ element: dataItem, column: titleColumn }\"\n ></ng-container>\n </span>\n }\n <span class=\"mm-card-actions\">\n <ng-container\n [ngTemplateOutlet]=\"rowActions\"\n [ngTemplateOutletContext]=\"{ dataItem: dataItem }\"\n ></ng-container>\n </span>\n </div>\n <div class=\"mm-card-fields\">\n @for (column of cardBodyColumns; track column.field) {\n @if (hasCardValue(dataItem, column)) {\n <div class=\"mm-card-field\">\n <span class=\"mm-card-label\">{{ getDisplayName(column) | pascalCase }}</span>\n <span class=\"mm-card-value\">\n <ng-container\n [ngTemplateOutlet]=\"cellContent\"\n [ngTemplateOutletContext]=\"{ element: dataItem, column: column }\"\n ></ng-container>\n </span>\n </div>\n }\n }\n </div>\n </div>\n </ng-template>\n </kendo-grid-column>\n }\n\n <kendo-grid-excel fileName=\"{{excelExportFileName}}\">\n @for (column of columns; track column.field) {\n <kendo-excelexport-column [field]=\"column.field\"\n [title]=\"getDisplayName(column) | pascalCase\">\n </kendo-excelexport-column>\n }\n </kendo-grid-excel>\n\n <kendo-grid-pdf fileName=\"{{pdfExportFileName}}\" paperSize=\"A4\" [repeatHeaders]=\"true\"\n [landscape]=\"true\">\n @for (column of columns; track column.field) {\n <kendo-grid-column [field]=\"column.field\"\n [filter]=\"getFilterType(column)\"\n [title]=\"getDisplayName(column) | pascalCase\"></kendo-grid-column>\n }\n <kendo-grid-pdf-margin top=\"1.5cm\" left=\"1cm\" right=\"1cm\" bottom=\"1.5cm\"></kendo-grid-pdf-margin>\n <ng-template kendoGridPDFTemplate let-pageNum=\"pageNum\" let-totalPages=\"totalPages\">\n <div class=\"page-template\">\n <div class=\"footer\" style=\"position: absolute; bottom: 0; width: 100%; text-align: center; font-size: 9px; color: #666;\">\n {{ getPdfPageText(pageNum, totalPages) }}\n </div>\n </div>\n </ng-template>\n </kendo-grid-pdf>\n</kendo-grid>\n\n<kendo-contextmenu\n #gridmenu\n [kendoMenuHierarchyBinding]=\"_contextMenuItems\"\n [textField]=\"['text']\"\n childrenField=\"items\"\n svgIconField=\"svgIcon\"\n separatorField=\"separator\"\n disabledField=\"disabled\"\n cssClassField=\"cssClass\"\n (popupClose)=\"onContextMenuClosed($event)\"\n (select)=\"onContextMenuSelect($event)\"\n></kendo-contextmenu>\n", styles: [":host{display:flex;flex-direction:column;min-height:0}:host>kendo-grid{flex:1;min-height:0}.mm-search-input{width:165px}:host ::ng-deep .k-grid th:has(input[type=checkbox]),:host ::ng-deep .k-grid td:has(>kendo-checkbox){text-overflow:clip}:host ::ng-deep .k-command-cell{white-space:nowrap;overflow:hidden;vertical-align:middle}:host ::ng-deep .k-command-cell .k-button{display:inline-flex;vertical-align:middle;margin:0}:host ::ng-deep .k-command-cell .k-button+.k-button{margin-left:4px}:host(.mm-list-view-narrow) ::ng-deep .k-grid-toolbar{flex-wrap:wrap;row-gap:6px}:host(.mm-list-view-narrow) ::ng-deep .k-grid-toolbar .mm-search-input{width:100%;order:10}:host(.mm-list-view-narrow) ::ng-deep .k-grid-toolbar kendo-grid-spacer{display:none}:host(.mm-list-view-narrow) ::ng-deep .k-grid-toolbar .mm-toolbar-commands{margin-left:auto}:host ::ng-deep .k-grid td.mm-col-align-right{text-align:right;font-variant-numeric:tabular-nums}:host ::ng-deep .k-grid td.mm-col-align-center{text-align:center}:host ::ng-deep .k-grid th.mm-col-align-right .k-cell-inner>.k-link{justify-content:flex-end}:host ::ng-deep .k-grid th.mm-col-align-center .k-cell-inner>.k-link{justify-content:center}:host ::ng-deep .k-filter-row .mm-filter-compact .k-filtercell-wrapper{gap:2px}:host ::ng-deep .k-filter-row .mm-filter-compact .k-datepicker>.k-input-button{display:none}:host ::ng-deep .k-filter-row .mm-filter-compact .k-input-inner{padding-left:4px;padding-right:4px;font-size:12px}:host ::ng-deep .k-filter-row .mm-filter-compact .k-filtercell-operator .k-dropdown-operator{width:26px;min-width:0}:host ::ng-deep .k-filter-row .mm-filter-compact .k-filtercell-operator .k-dropdown-operator .k-input-button{padding:2px}.mm-cell-truncate{display:block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.status-icons-cell{display:inline-flex;align-items:center;gap:8px}.status-icons-cell .status-icon{display:inline-flex;align-items:center;justify-content:center;cursor:default}.status-icons-cell .status-icon kendo-svg-icon{width:18px;height:18px}:host ::ng-deep .filter-status-item{display:inline-flex;align-items:center;gap:10px}:host ::ng-deep .status-filter-dropdown .k-input-value-text{font-size:0}:host ::ng-deep .status-filter-dropdown .k-input-value-text .filter-status-item,:host ::ng-deep .status-filter-dropdown .k-input-value-text>span{font-size:14px}.numeric-range-filter{display:flex;align-items:center;gap:4px}.numeric-range-filter kendo-numerictextbox{flex:1;min-width:0}.numeric-range-filter .range-separator{flex-shrink:0;opacity:.6}.cron-expression-cell{display:flex;flex-direction:column;gap:2px}.cron-expression-cell .cron-expression{font-family:Roboto Mono,Consolas,monospace;font-size:.85em;background:var(--mm-cron-code-bg, #eeeeee);color:var(--mm-cron-code-text, #333333);padding:2px 6px;border-radius:3px}.cron-expression-cell .cron-description{font-size:.8em;color:var(--mm-cron-text-secondary, #666666)}.badge-cell{display:inline-block;padding:2px 10px;font-size:.75rem;font-weight:600;border-radius:12px;border:1px solid var(--mm-badge-default-border, transparent);background:var(--mm-badge-default-bg, rgba(127, 127, 127, .15));color:var(--mm-badge-default-color, inherit);white-space:nowrap;line-height:1.4}.badge-cell-default{opacity:.7;font-style:italic}.progress-bar-cell{display:inline-flex;align-items:center;gap:8px;width:100%}.progress-bar-cell .progress-bar-track{flex:1;height:8px;background:var(--mm-progress-track-bg, rgba(255, 255, 255, .1));border-radius:4px;overflow:hidden}.progress-bar-cell .progress-bar-fill{display:block;height:100%;background:var(--mm-progress-fill-color, #10b981);border-radius:4px;transition:width .3s ease}.progress-bar-cell .progress-bar-label{min-width:40px;text-align:right;font-size:.85em}:host-context(.k-pdf-export) .k-grid,:host-context(.k-pdf-export) .k-grid-header,:host-context(.k-pdf-export) .k-grid-content,:host-context(.k-pdf-export) .k-grid-header-wrap,:host-context(.k-pdf-export) .k-grid-content-locked,:host-context(.k-pdf-export) .k-grid-header-locked{background:#fff!important;background-color:#fff!important;background-image:none!important;color:#000!important;border-color:#000!important}:host-context(.k-pdf-export) .k-grid{border:1px solid #000!important;font-family:Arial,sans-serif!important;font-size:10px!important}:host-context(.k-pdf-export) .k-grid-header th,:host-context(.k-pdf-export) .k-header,:host-context(.k-pdf-export) .k-grid th{background:#f0f0f0!important;background-color:#f0f0f0!important;background-image:none!important;color:#000!important;border:1px solid #000!important;font-weight:700!important;padding:6px 8px!important;text-transform:none!important;letter-spacing:normal!important}:host-context(.k-pdf-export) .k-grid td,:host-context(.k-pdf-export) .k-grid-content td,:host-context(.k-pdf-export) .k-master-row td{background:#fff!important;background-color:#fff!important;background-image:none!important;color:#000!important;border:1px solid #000!important;padding:4px 8px!important}:host-context(.k-pdf-export) .k-grid tr,:host-context(.k-pdf-export) .k-master-row,:host-context(.k-pdf-export) .k-alt{background:#fff!important;background-color:#fff!important;background-image:none!important}:host-context(.k-pdf-export) .k-grid tr:hover,:host-context(.k-pdf-export) .k-grid tr.k-selected,:host-context(.k-pdf-export) .k-grid td:hover{background:#fff!important;background-color:#fff!important}:host-context(.k-pdf-export) .k-grid-toolbar,:host-context(.k-pdf-export) .k-pager,:host-context(.k-pdf-export) .k-grid-pager,:host-context(.k-pdf-export) .k-command-cell,:host-context(.k-pdf-export) .k-checkbox-column,:host-context(.k-pdf-export) kendo-grid-checkbox-column,:host-context(.k-pdf-export) .status-icons-cell,:host-context(.k-pdf-export) .status-icon,:host-context(.k-pdf-export) kendo-svg-icon{display:none!important}:host-context(.k-pdf-export) .badge-cell{background:transparent!important;border:none!important;color:inherit!important;padding:0!important;border-radius:0!important;text-transform:none!important;font-weight:inherit!important;font-size:inherit!important}:host(.mm-list-view-cards) ::ng-deep .k-grid-header{display:none}:host(.mm-list-view-cards) ::ng-deep .k-grid td,:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-td{border:none;background:transparent;padding:4px 2px;white-space:normal}:host(.mm-list-view-cards) ::ng-deep .k-grid td:has(>kendo-checkbox),:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-td:has(>kendo-checkbox){padding-left:2px;padding-right:0}:host(.mm-list-view-cards) ::ng-deep .k-grid colgroup>col:first-child:not(:only-child){width:32px!important}:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-row:hover,:host(.mm-list-view-cards) ::ng-deep .k-grid tr:hover{background:transparent}:host(.mm-list-view-cards) ::ng-deep .k-grid tr.k-selected .mm-card,:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-row.k-selected .mm-card{border-color:var(--kendo-color-primary, #ff6358)}:host(.mm-list-view-cards) ::ng-deep .k-grid tr.k-selected>td,:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-row.k-selected>.k-table-td{background:transparent}.mm-card{display:flex;flex-direction:column;gap:6px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:8px;padding:10px 12px}.mm-card-header{display:flex;align-items:center;gap:8px}.mm-card-title{flex:1;min-width:0;font-weight:600;overflow-wrap:anywhere}.mm-card-actions{display:flex;align-items:center;gap:2px;flex-shrink:0}.mm-card-fields{display:flex;flex-direction:column;gap:3px}.mm-card-field{display:flex;align-items:baseline;gap:8px;font-size:.9em}.mm-card-label{flex:0 0 38%;max-width:160px;color:var(--kendo-color-subtle, #666666);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mm-card-value{flex:1;min-width:0;overflow-wrap:anywhere}::ng-deep .k-grid-toolbar .mm-toolbar-filters{display:flex;align-items:center;gap:12px;flex-wrap:wrap}::ng-deep .k-grid-toolbar .mm-toolbar-filters-separator{align-self:center;height:22px;margin:0 4px;border-left:1px solid var(--theme-border-subtle, rgba(0, 0, 0, .16));border-top:none;width:0}::ng-deep .k-menu-group .mm-list-view-menu-item--danger>.k-link,::ng-deep .k-menu-group .mm-list-view-menu-item--danger>.k-menu-link{color:var(--theme-status-error, #c0385f)}.mm-list-view-row-actions{display:inline-flex;align-items:center;gap:4px;white-space:nowrap;vertical-align:middle}.mm-list-view-row-actions .mm-list-view-row-actions__more:focus-visible{outline:none;box-shadow:var(--theme-focus-ring, 0 0 0 2px #ffffff, 0 0 0 4px #2e8473)}.boolean-icon{display:inline-flex;align-items:center}.boolean-icon.boolean-icon--true{color:var(--mm-list-boolean-true-color, var(--kendo-color-success, #2fb37a))}.boolean-icon.boolean-icon--false{color:var(--mm-list-boolean-false-color, var(--kendo-color-subtle, #8a94a0))}\n"], dependencies: [{ kind: "component", type: GridComponent, selector: "kendo-grid", inputs: ["data", "pageSize", "height", "rowHeight", "adaptiveMode", "detailRowHeight", "skip", "scrollable", "selectable", "sort", "size", "trackBy", "filter", "group", "virtualColumns", "filterable", "sortable", "pageable", "groupable", "gridResizable", "rowReorderable", "navigable", "autoSize", "rowClass", "rowSticky", "rowSelected", "isRowSelectable", "cellSelected", "resizable", "reorderable", "loading", "columnMenu", "hideHeader", "showInactiveTools", "isDetailExpanded", "isGroupExpanded", "dataLayoutMode", "pinnable", "pinnedTopRows", "pinnedBottomRows", "isRowPinnable"], outputs: ["filterChange", "pageChange", "groupChange", "sortChange", "selectionChange", "rowReorder", "rowPinChange", "dataStateChange", "gridStateChange", "groupExpand", "groupCollapse", "detailExpand", "detailCollapse", "edit", "cancel", "save", "remove", "add", "cellClose", "cellClick", "pdfExport", "excelExport", "csvExport", "columnResize", "columnReorder", "columnVisibilityChange", "columnLockedChange", "columnStickyChange", "scrollBottom", "contentScroll"], exportAs: ["kendoGrid"] }, { kind: "directive", type: MmListViewDataBindingDirective, selector: "[mmListViewDataBinding]" }, { kind: "component", type: ColumnComponent, selector: "kendo-grid-column", inputs: ["field", "format", "sortable", "groupable", "editor", "filter", "filterVariant", "filterable", "editable"] }, { kind: "directive", type: ToolbarTemplateDirective, selector: "[kendoGridToolbarTemplate]", inputs: ["position"] }, { kind: "component", type: GridSpacerComponent, selector: "kendo-grid-spacer", inputs: ["width"] }, { kind: "ngmodule", type: ExcelModule }, { kind: "component", type: i1$2.ExcelComponent, selector: "kendo-grid-excel", inputs: ["fileName", "filterable", "creator", "date", "forceProxy", "proxyURL", "fetchData", "paddingCellOptions", "headerPaddingCellOptions", "collapsible"], outputs: ["fileCreated"] }, { kind: "component", type: i1$2.ExcelCommandDirective, selector: "[kendoGridExcelCommand]" }, { kind: "component", type: i2$1.ColumnComponent, selector: "kendo-excelexport-column", inputs: ["field", "cellOptions", "groupHeaderCellOptions", "groupFooterCellOptions", "footerCellOptions"] }, { kind: "ngmodule", type: PDFModule }, { kind: "component", type: i1$2.PDFComponent, selector: "kendo-grid-pdf", inputs: ["allPages", "delay"] }, { kind: "component", type: i1$2.PDFMarginComponent, selector: "kendo-grid-pdf-margin" }, { kind: "component", type: i1$2.PDFCommandDirective, selector: "[kendoGridPDFCommand]" }, { kind: "directive", type: i1$2.PDFTemplateDirective, selector: "[kendoGridPDFTemplate]" }, { kind: "component", type: ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "component", type: DropDownButtonComponent, selector: "kendo-dropdownbutton", inputs: ["arrowIcon", "icon", "svgIcon", "iconClass", "imageUrl", "textField", "data", "size", "rounded", "fillMode", "themeColor", "buttonAttributes"], outputs: ["itemClick", "focus", "blur"], exportAs: ["kendoDropDownButton"] }, { kind: "component", type: SplitButtonComponent, selector: "kendo-splitbutton", inputs: ["text", "icon", "svgIcon", "iconClass", "type", "imageUrl", "size", "rounded", "fillMode", "themeColor", "disabled", "popupSettings", "tabIndex", "textField", "data", "arrowButtonClass", "arrowButtonIcon", "arrowButtonSvgIcon", "buttonAttributes"], outputs: ["buttonClick", "itemClick", "focus", "blur", "open", "close"], exportAs: ["kendoSplitButton"] }, { kind: "component", type: CommandColumnComponent, selector: "kendo-grid-command-column" }, { kind: "directive", type: CellTemplateDirective, selector: "[kendoGridCellTemplate]" }, { kind: "component", type: ContextMenuComponent, selector: "kendo-contextmenu", inputs: ["showOn", "target", "filter", "alignToAnchor", "vertical", "popupAnimate", "popupAlign", "anchorAlign", "collision", "appendTo", "ariaLabel"], outputs: ["popupOpen", "popupClose", "select", "open", "close"], exportAs: ["kendoContextMenu"] }, { kind: "directive", type: HierarchyBindingDirective, selector: "[kendoMenuHierarchyBinding]", inputs: ["kendoMenuHierarchyBinding", "textField", "urlField", "iconField", "svgIconField", "disabledField", "cssClassField", "cssStyleField", "separatorField", "childrenField"], exportAs: ["kendoMenuHierarchyBinding"] }, { kind: "component", type: CheckboxColumnComponent, selector: "kendo-grid-checkbox-column", inputs: ["showSelectAll", "showDisabledCheckbox"] }, { kind: "component", type: CheckBoxComponent, selector: "kendo-checkbox", inputs: ["checkedState", "rounded"], outputs: ["checkedStateChange"], exportAs: ["kendoCheckBox"] }, { kind: "component", type: SeparatorComponent, selector: "kendo-separator", inputs: ["orientation"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i2.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon", "variant"], exportAs: ["kendoSVGIcon"] }, { kind: "component", type: CustomMessagesComponent, selector: "kendo-grid-messages" }, { kind: "directive", type: FilterCellTemplateDirective, selector: "[kendoGridFilterCellTemplate]" }, { kind: "component", type: StringFilterCellComponent, selector: "kendo-grid-string-filter-cell", inputs: ["filterDelay", "showOperators", "placeholder"] }, { kind: "component", type: NumericFilterCellComponent, selector: "kendo-grid-numeric-filter-cell", inputs: ["filterDelay", "showOperators", "placeholder"] }, { kind: "component", type: BooleanFilterCellComponent, selector: "kendo-grid-boolean-filter-cell" }, { kind: "component", type: DateFilterCellComponent, selector: "kendo-grid-date-filter-cell", inputs: ["showOperators"] }, { kind: "component", type: DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "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: "directive", type: ValueTemplateDirective, selector: "[kendoDropDownListValueTemplate],[kendoDropDownTreeValueTemplate]" }, { kind: "directive", type: ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: NumericTextBoxComponent, selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ActionButtonComponent, selector: "mm-action-button", inputs: ["action", "targetLabel", "context", "display", "primary"], outputs: ["triggered"] }, { kind: "directive", type: NoRecordsTemplateDirective, selector: "[kendoGridNoRecordsTemplate]" }, { kind: "component", type: EmptyStateComponent, selector: "mm-empty-state", inputs: ["variant", "heading", "text", "size", "showRetry", "retryLabel"], outputs: ["retry"] }, { kind: "pipe", type: PascalCasePipe, name: "pascalCase" }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "pipe", type: DecimalPipe, name: "number" }, { kind: "pipe", type: BytesToSizePipe, name: "bytesToSize" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2860
4016
|
}
|
|
2861
4017
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ListViewComponent, decorators: [{
|
|
2862
4018
|
type: Component,
|
|
@@ -2894,8 +4050,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
2894
4050
|
ItemTemplateDirective,
|
|
2895
4051
|
NumericTextBoxComponent,
|
|
2896
4052
|
NgComponentOutlet,
|
|
2897
|
-
NgTemplateOutlet
|
|
2898
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Shared cell renderer (AB#4930): used by the regular grid columns AND the\n card-mode body lines, so both render every dataType identically. -->\n<ng-template #cellContent let-element=\"element\" let-column=\"column\">\n @if (column.formatter) {\n {{ getFormattedValue(element, column) }}\n } @else {\n @switch (column.dataType) {\n @case ('boolean') {\n <kendo-checkbox type=\"checkbox\" [checkedState]=\"$any(getValue(element, column))\" [disabled]=\"true\" />\n }\n @case ('iso8601') {\n {{ $any(getValue(element, column)) | date:column.format }}\n }\n @case ('bytes') {\n {{ $any(getValue(element, column)) | bytesToSize }}\n }\n @case ('statusIcons') {\n <span class=\"status-icons-cell\">\n @for (fieldConfig of getStatusFields(column); track fieldConfig.field) {\n @if (getStatusIconMapping(element, fieldConfig); as mapping) {\n <span\n class=\"status-icon\"\n [title]=\"mapping.tooltip\"\n [style.color]=\"mapping.color\">\n <kendo-svg-icon [icon]=\"mapping.icon\"></kendo-svg-icon>\n </span>\n }\n }\n </span>\n }\n @case ('cronExpression') {\n <span class=\"cron-expression-cell\">\n <code class=\"cron-expression\">{{ getValue(element, column) }}</code>\n <span class=\"cron-description\">{{ getCronHumanReadable(getValue(element, column)) }}</span>\n </span>\n }\n @case ('numeric') {\n {{ $any(getValue(element, column)) | number:column.format }}\n }\n @case ('progressBar') {\n <span class=\"progress-bar-cell\">\n <span class=\"progress-bar-track\">\n <span class=\"progress-bar-fill\" [style.width.%]=\"getValue(element, column)\"></span>\n </span>\n <span class=\"progress-bar-label\">{{ getValue(element, column) }}%</span>\n </span>\n }\n @case ('badge') {\n @if (getBadgeMapping(element, column); as badge) {\n <span\n class=\"badge-cell\"\n [title]=\"badge.tooltip ?? ''\"\n [style.color]=\"badge.color\"\n [style.background]=\"badge.backgroundColor\"\n [style.borderColor]=\"badge.borderColor ?? badge.backgroundColor\">\n {{ badge.label ?? getValue(element, column) }}\n </span>\n } @else if (!column.badgeHideUnmapped) {\n <span class=\"badge-cell badge-cell-default\">{{ getValue(element, column) }}</span>\n }\n }\n @case ('component') {\n @if (column.cellComponent) {\n <ng-container *ngComponentOutlet=\"column.cellComponent; inputs: getCellInputs(element, column)\" />\n }\n }\n @default {\n {{ getValue(element, column) }}\n }\n }\n }\n</ng-template>\n\n<!-- Row actions (AB#4930): shared by the actions column and the card header. -->\n<ng-template #rowActions let-dataItem=\"dataItem\">\n @if (_actionMenuItems.length > 0) {\n @for (menuItem of _actionMenuItems; track menuItem.text) {\n @if (!menuItem.separator && getMenuItemVisible(menuItem, dataItem)) {\n <button kendoButton themeColor=\"primary\" fillMode=\"flat\"\n [svgIcon]=\"menuItem.svgIcon!\"\n [title]=\"menuItem.text ?? ''\"\n [disabled]=\"getMenuItemDisabled(menuItem, dataItem) || isLoading()\"\n (click)=\"onSelectOptionActionItem($event, dataItem, menuItem)\">\n </button>\n }\n }\n }\n\n @if (_contextMenuItems.length > 0 && contextMenuType == 'actionMenu') {\n <button kendoButton themeColor=\"primary\" fillMode=\"flat\" [svgIcon]=\"moreVerticalIcon\" [disabled]=\"isLoading()\" (click)=\"onContextMenu(dataItem, $event)\"></button>\n }\n</ng-template>\n\n<kendo-grid\n mmListViewDataBinding\n [loading]=\"isLoading()\"\n [pageSize]=\"effectivePageSize()\" [selectable]=\"selectable\" (pageChange)=\"onPageChange($event)\"\n [skip]=\"skip\" (selectionChange)=\"onRowSelect($event)\" (cellClick)=\"onCellClick($event)\"\n [pageable]=\"effectivePageable\"\n [sortable]=\"sortable\"\n [filterable]=\"_showRowFilter && !isCardMode\"\n [resizable]=\"resizable\"\n [rowClass]=\"rowClass ?? defaultRowClass\"\n>\n <kendo-grid-messages\n [pagerItemsPerPage]=\"_messages.pagerItemsPerPage\"\n [pagerOf]=\"_messages.pagerOf\"\n [pagerItems]=\"_messages.pagerItems\"\n [pagerPage]=\"_messages.pagerPage\"\n [pagerFirstPage]=\"_messages.pagerFirstPage\"\n [pagerLastPage]=\"_messages.pagerLastPage\"\n [pagerPreviousPage]=\"_messages.pagerPreviousPage\"\n [pagerNextPage]=\"_messages.pagerNextPage\"\n [noRecords]=\"_messages.noRecords\"\n ></kendo-grid-messages>\n <ng-template kendoGridToolbarTemplate>\n\n @for (commandItem of leftToolbarActions; track commandItem.id) {\n @if (commandItem) {\n @switch (commandItem.type) {\n @case ('link') {\n @if (commandItem.children && commandItem.children.length > 0 && commandItem.onClick) {\n <kendo-splitbutton\n [data]=\"commandItem.children\"\n [svgIcon]=\"$any(commandItem.svgIcon)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n [textField]=\"'text'\"\n themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n (buttonClick)=\"onToolbarCommand(commandItem)\"\n (itemClick)=\"onToolbarDropdownItemClick($event)\">\n {{ commandItem.text }}\n </kendo-splitbutton>\n } @else if (commandItem.children && commandItem.children.length > 0) {\n <kendo-dropdownbutton\n [data]=\"commandItem.children\"\n [svgIcon]=\"$any(commandItem.svgIcon)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n [textField]=\"'text'\"\n themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n (itemClick)=\"onToolbarDropdownItemClick($event)\">\n {{ commandItem.text }}\n </kendo-dropdownbutton>\n } @else if (commandItem.svgIcon) {\n <button kendoTooltip kendoButton themeColor=\"primary\" [svgIcon]=\"commandItem.svgIcon\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n (click)=\"onToolbarCommand(commandItem)\">{{ commandItem.text }}\n </button>\n } @else {\n <button kendoTooltip kendoButton themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n (click)=\"onToolbarCommand(commandItem)\">{{ commandItem.text }}\n </button>\n }\n }\n @case ('separator'){\n <kendo-separator></kendo-separator>\n }\n }\n }\n }\n\n <!-- Host scope/filter controls (view switches and the like). They steer the\n table, so they sit in the table's own band instead of a second strip\n stacked above the grid. -->\n @if (filtersTemplate) {\n <!-- A rule between the host's actions and its filters: both sit on the\n left of the same band, but one acts on records and the other changes\n what the list shows. Only drawn when actions precede them. -->\n @if (leftToolbarActions.length > 0) {\n <kendo-separator class=\"mm-toolbar-filters-separator\"></kendo-separator>\n }\n <div class=\"mm-toolbar-filters\">\n <ng-container [ngTemplateOutlet]=\"filtersTemplate.templateRef\"></ng-container>\n </div>\n }\n\n <kendo-grid-spacer></kendo-grid-spacer>\n @if (searchTextBoxEnabled) {\n <input\n class=\"k-textbox k-input k-input-md k-rounded-md mm-search-input\"\n [placeholder]=\"_messages.searchPlaceholder\"\n [value]=\"searchValue\"\n (input)=\"onFilter($any($event.target).value || null)\"\n />\n }\n\n @for (commandItem of rightToolbarActions; track commandItem.id) {\n @if (commandItem) {\n @switch (commandItem.type) {\n @case ('link') {\n @if (commandItem.children && commandItem.children.length > 0 && commandItem.onClick) {\n <kendo-splitbutton\n [data]=\"commandItem.children\"\n [svgIcon]=\"$any(commandItem.svgIcon)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n [textField]=\"'text'\"\n themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n (buttonClick)=\"onToolbarCommand(commandItem)\"\n (itemClick)=\"onToolbarDropdownItemClick($event)\">\n {{ commandItem.text }}\n </kendo-splitbutton>\n } @else if (commandItem.children && commandItem.children.length > 0) {\n <kendo-dropdownbutton\n [data]=\"commandItem.children\"\n [svgIcon]=\"$any(commandItem.svgIcon)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n [textField]=\"'text'\"\n themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n (itemClick)=\"onToolbarDropdownItemClick($event)\">\n {{ commandItem.text }}\n </kendo-dropdownbutton>\n } @else if (commandItem.svgIcon) {\n <button kendoTooltip kendoButton themeColor=\"primary\" [svgIcon]=\"commandItem.svgIcon\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n (click)=\"onToolbarCommand(commandItem)\">{{ commandItem.text }}\n </button>\n } @else {\n <button kendoTooltip kendoButton themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n (click)=\"onToolbarCommand(commandItem)\">{{ commandItem.text }}\n </button>\n }\n }\n @case ('separator'){\n <kendo-separator></kendo-separator>\n }\n }\n }\n }\n\n <!-- Commands: laid out while there is room, collapsed into one menu once\n the list gets narrow. Same behaviour in every host app \u2014 a list this\n wide has space for five buttons, and a list this narrow does not. -->\n @if (commandsCollapsed) {\n <kendo-dropdownbutton class=\"mm-toolbar-commands\" [data]=\"toolbarCommands\" textField=\"text\" [svgIcon]=\"commandsIcon\"\n fillMode=\"flat\" themeColor=\"primary\" [disabled]=\"isLoading()\"\n [title]=\"_messages.commands\"\n (itemClick)=\"onCommand($any($event).id)\"></kendo-dropdownbutton>\n } @else {\n @for (command of toolbarCommands; track command.id) {\n @switch (command.id) {\n @case ('excel') {\n <button kendoTooltip kendoGridExcelCommand themeColor=\"primary\" fillMode=\"flat\"\n [svgIcon]=\"command.svgIcon\" [disabled]=\"isLoading()\" [title]=\"command.text\"></button>\n }\n @case ('pdf') {\n <button kendoTooltip kendoGridPDFCommand themeColor=\"primary\" fillMode=\"flat\"\n [svgIcon]=\"command.svgIcon\" [disabled]=\"isLoading()\" [title]=\"command.text\"></button>\n }\n @default {\n <button kendoTooltip kendoButton themeColor=\"primary\" fillMode=\"flat\"\n [svgIcon]=\"command.svgIcon\" (click)=\"onCommand(command.id)\"\n [disabled]=\"isLoading()\" [title]=\"command.text\"></button>\n }\n }\n }\n }\n </ng-template>\n\n @if (showCheckboxColumn()) {\n <kendo-grid-checkbox-column [showSelectAll]=\"showRowSelectAllCheckBox\"\n [width]=\"effectiveCheckboxColumnWidth\"></kendo-grid-checkbox-column>\n }\n\n @if (!isCardMode) {\n @for (column of columns; track column.field) {\n <kendo-grid-column [field]=\"column.field\"\n [filter]=\"getFilterType(column)\" format=\"{{column.format}}\"\n [width]=\"$any(getEffectiveWidth(column))\"\n [minResizableWidth]=\"column.minWidth ?? 10\"\n [hidden]=\"isColumnHidden(column)\"\n [sortable]=\"column.sortable !== false\"\n [filterable]=\"column.filterable !== false\"\n [class]=\"alignClass(column)\"\n [headerClass]=\"alignClass(column)\"\n [title]=\"getDisplayName(column) | pascalCase\">\n <ng-template kendoGridFilterCellTemplate let-filter let-gridColumn=\"column\">\n @if (hasFilterOptions(column)) {\n <kendo-dropdownlist\n class=\"status-filter-dropdown\"\n [data]=\"column.filterOptions!\"\n textField=\"text\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n [value]=\"getSelectedFilterValue(column)\"\n [defaultItem]=\"{ text: '', value: null }\"\n [popupSettings]=\"{ width: 'auto' }\"\n (valueChange)=\"onDropdownFilter($event, column)\"\n >\n <ng-template kendoDropDownListValueTemplate let-dataItem>\n @if (dataItem?.value && column.statusMapping?.[dataItem.value]; as mapping) {\n <span class=\"filter-status-item\">\n <span [style.color]=\"mapping.color\">\n <kendo-svg-icon [icon]=\"mapping.icon\" [size]=\"'small'\"></kendo-svg-icon>\n </span>\n </span>\n } @else if (dataItem?.text) {\n <span>{{ dataItem.text }}</span>\n }\n </ng-template>\n <ng-template kendoDropDownListItemTemplate let-dataItem>\n @if (dataItem?.value && column.statusMapping?.[dataItem.value]; as mapping) {\n <span style=\"display: inline-flex; align-items: center; gap: 8px;\">\n <span [style.color]=\"mapping.color\">\n <kendo-svg-icon [icon]=\"mapping.icon\" [size]=\"'small'\"></kendo-svg-icon>\n </span>\n <span>{{ dataItem.text }}</span>\n </span>\n } @else {\n <span>{{ dataItem.text }}</span>\n }\n </ng-template>\n </kendo-dropdownlist>\n } @else if (column.dataType === 'numericRange') {\n <span class=\"numeric-range-filter\">\n <kendo-numerictextbox\n [value]=\"$any(getRangeFilterValue(column, 'gte'))\"\n (valueChange)=\"onRangeFilterChange($event, column, 'gte')\"\n [spinners]=\"false\"\n [decimals]=\"2\"\n placeholder=\"From\"\n size=\"small\"\n ></kendo-numerictextbox>\n <span class=\"range-separator\">\u2013</span>\n <kendo-numerictextbox\n [value]=\"$any(getRangeFilterValue(column, 'lte'))\"\n (valueChange)=\"onRangeFilterChange($event, column, 'lte')\"\n [spinners]=\"false\"\n [decimals]=\"2\"\n placeholder=\"To\"\n size=\"small\"\n ></kendo-numerictextbox>\n </span>\n } @else {\n @switch (getFilterType(column)) {\n @case ('numeric') {\n <kendo-grid-numeric-filter-cell [column]=\"gridColumn\" [filter]=\"filter\" [showOperators]=\"true\" [operator]=\"column.filterOperator || 'eq'\"\n [spinners]=\"!isCompactFilter(column)\" [class.mm-filter-compact]=\"isCompactFilter(column)\"></kendo-grid-numeric-filter-cell>\n }\n @case ('boolean') {\n <kendo-grid-boolean-filter-cell [column]=\"gridColumn\" [filter]=\"filter\"></kendo-grid-boolean-filter-cell>\n }\n @case ('date') {\n <kendo-grid-date-filter-cell [column]=\"gridColumn\" [filter]=\"filter\" [showOperators]=\"true\" [operator]=\"column.filterOperator || 'eq'\"\n [class.mm-filter-compact]=\"isCompactFilter(column)\"></kendo-grid-date-filter-cell>\n }\n @default {\n <kendo-grid-string-filter-cell [column]=\"gridColumn\" [filter]=\"filter\" [showOperators]=\"false\"></kendo-grid-string-filter-cell>\n }\n }\n }\n </ng-template>\n <ng-template kendoGridCellTemplate let-dataItem>\n <span\n [class.mm-cell-truncate]=\"column.truncate\"\n [attr.title]=\"column.truncate ? getCellTitle(dataItem, column) : null\">\n <ng-container\n [ngTemplateOutlet]=\"cellContent\"\n [ngTemplateOutletContext]=\"{ element: dataItem, column: column }\"\n ></ng-container>\n </span>\n </ng-template>\n </kendo-grid-column>\n }\n\n @if (_actionMenuItems.length > 0 || (_contextMenuItems.length > 0 && contextMenuType == 'actionMenu')) {\n <kendo-grid-command-column [title]=\"_messages.actionsColumnTitle\" [width]=\"effectiveActionsColumnWidth\">\n <ng-template kendoGridCellTemplate let-dataItem>\n <ng-container\n [ngTemplateOutlet]=\"rowActions\"\n [ngTemplateOutletContext]=\"{ dataItem: dataItem }\"\n ></ng-container>\n </ng-template>\n </kendo-grid-command-column>\n }\n } @else {\n <!-- Card mode (AB#4930): one full-width column renders the whole row as a\n stacked card \u2014 title from the first column, labeled lines for the\n rest (empty values skipped), row actions in the card header. -->\n <kendo-grid-column [sortable]=\"false\" [filterable]=\"false\" title=\"\">\n <ng-template kendoGridCellTemplate let-dataItem>\n <div class=\"mm-card\">\n <div class=\"mm-card-header\">\n @if (cardTitleColumn; as titleColumn) {\n <span class=\"mm-card-title\">\n <ng-container\n [ngTemplateOutlet]=\"cellContent\"\n [ngTemplateOutletContext]=\"{ element: dataItem, column: titleColumn }\"\n ></ng-container>\n </span>\n }\n <span class=\"mm-card-actions\">\n <ng-container\n [ngTemplateOutlet]=\"rowActions\"\n [ngTemplateOutletContext]=\"{ dataItem: dataItem }\"\n ></ng-container>\n </span>\n </div>\n <div class=\"mm-card-fields\">\n @for (column of cardBodyColumns; track column.field) {\n @if (hasCardValue(dataItem, column)) {\n <div class=\"mm-card-field\">\n <span class=\"mm-card-label\">{{ getDisplayName(column) | pascalCase }}</span>\n <span class=\"mm-card-value\">\n <ng-container\n [ngTemplateOutlet]=\"cellContent\"\n [ngTemplateOutletContext]=\"{ element: dataItem, column: column }\"\n ></ng-container>\n </span>\n </div>\n }\n }\n </div>\n </div>\n </ng-template>\n </kendo-grid-column>\n }\n\n <kendo-grid-excel fileName=\"{{excelExportFileName}}\">\n @for (column of columns; track column.field) {\n <kendo-excelexport-column [field]=\"column.field\"\n [title]=\"getDisplayName(column) | pascalCase\">\n </kendo-excelexport-column>\n }\n </kendo-grid-excel>\n\n <kendo-grid-pdf fileName=\"{{pdfExportFileName}}\" paperSize=\"A4\" [repeatHeaders]=\"true\"\n [landscape]=\"true\">\n @for (column of columns; track column.field) {\n <kendo-grid-column [field]=\"column.field\"\n [filter]=\"getFilterType(column)\"\n [title]=\"getDisplayName(column) | pascalCase\"></kendo-grid-column>\n }\n <kendo-grid-pdf-margin top=\"1.5cm\" left=\"1cm\" right=\"1cm\" bottom=\"1.5cm\"></kendo-grid-pdf-margin>\n <ng-template kendoGridPDFTemplate let-pageNum=\"pageNum\" let-totalPages=\"totalPages\">\n <div class=\"page-template\">\n <div class=\"footer\" style=\"position: absolute; bottom: 0; width: 100%; text-align: center; font-size: 9px; color: #666;\">\n {{ getPdfPageText(pageNum, totalPages) }}\n </div>\n </div>\n </ng-template>\n </kendo-grid-pdf>\n</kendo-grid>\n\n<kendo-contextmenu\n #gridmenu\n [kendoMenuHierarchyBinding]=\"_contextMenuItems\"\n [textField]=\"['text']\"\n childrenField=\"items\"\n svgIconField=\"svgIcon\"\n separatorField=\"separator\"\n disabledField=\"disabled\"\n (popupClose)=\"onContextMenuClosed($event)\"\n (select)=\"onContextMenuSelect($event)\"\n></kendo-contextmenu>\n", styles: [":host{display:flex;flex-direction:column;min-height:0}:host>kendo-grid{flex:1;min-height:0}.mm-search-input{width:165px}:host ::ng-deep .k-grid th:has(input[type=checkbox]),:host ::ng-deep .k-grid td:has(>kendo-checkbox){text-overflow:clip}:host ::ng-deep .k-command-cell{white-space:nowrap;overflow:hidden;vertical-align:middle}:host ::ng-deep .k-command-cell .k-button{display:inline-flex;vertical-align:middle;margin:0}:host ::ng-deep .k-command-cell .k-button+.k-button{margin-left:4px}:host(.mm-list-view-narrow) ::ng-deep .k-grid-toolbar{flex-wrap:wrap;row-gap:6px}:host(.mm-list-view-narrow) ::ng-deep .k-grid-toolbar .mm-search-input{width:100%;order:10}:host(.mm-list-view-narrow) ::ng-deep .k-grid-toolbar kendo-grid-spacer{display:none}:host(.mm-list-view-narrow) ::ng-deep .k-grid-toolbar .mm-toolbar-commands{margin-left:auto}:host ::ng-deep .k-grid td.mm-col-align-right{text-align:right;font-variant-numeric:tabular-nums}:host ::ng-deep .k-grid td.mm-col-align-center{text-align:center}:host ::ng-deep .k-grid th.mm-col-align-right .k-cell-inner>.k-link{justify-content:flex-end}:host ::ng-deep .k-grid th.mm-col-align-center .k-cell-inner>.k-link{justify-content:center}:host ::ng-deep .k-filter-row .mm-filter-compact .k-filtercell-wrapper{gap:2px}:host ::ng-deep .k-filter-row .mm-filter-compact .k-datepicker>.k-input-button{display:none}:host ::ng-deep .k-filter-row .mm-filter-compact .k-input-inner{padding-left:4px;padding-right:4px;font-size:12px}:host ::ng-deep .k-filter-row .mm-filter-compact .k-filtercell-operator .k-dropdown-operator{width:26px;min-width:0}:host ::ng-deep .k-filter-row .mm-filter-compact .k-filtercell-operator .k-dropdown-operator .k-input-button{padding:2px}.mm-cell-truncate{display:block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.status-icons-cell{display:inline-flex;align-items:center;gap:8px}.status-icons-cell .status-icon{display:inline-flex;align-items:center;justify-content:center;cursor:default}.status-icons-cell .status-icon kendo-svg-icon{width:18px;height:18px}:host ::ng-deep .filter-status-item{display:inline-flex;align-items:center;gap:10px}:host ::ng-deep .status-filter-dropdown .k-input-value-text{font-size:0}:host ::ng-deep .status-filter-dropdown .k-input-value-text .filter-status-item,:host ::ng-deep .status-filter-dropdown .k-input-value-text>span{font-size:14px}.numeric-range-filter{display:flex;align-items:center;gap:4px}.numeric-range-filter kendo-numerictextbox{flex:1;min-width:0}.numeric-range-filter .range-separator{flex-shrink:0;opacity:.6}.cron-expression-cell{display:flex;flex-direction:column;gap:2px}.cron-expression-cell .cron-expression{font-family:Roboto Mono,Consolas,monospace;font-size:.85em;background:var(--mm-cron-code-bg, #eeeeee);color:var(--mm-cron-code-text, #333333);padding:2px 6px;border-radius:3px}.cron-expression-cell .cron-description{font-size:.8em;color:var(--mm-cron-text-secondary, #666666)}.badge-cell{display:inline-block;padding:2px 10px;font-size:.75rem;font-weight:600;letter-spacing:.5px;text-transform:uppercase;border-radius:12px;border:1px solid var(--mm-badge-default-border, transparent);background:var(--mm-badge-default-bg, rgba(127, 127, 127, .15));color:var(--mm-badge-default-color, inherit);white-space:nowrap;line-height:1.4}.badge-cell-default{opacity:.7;font-style:italic}.progress-bar-cell{display:inline-flex;align-items:center;gap:8px;width:100%}.progress-bar-cell .progress-bar-track{flex:1;height:8px;background:var(--mm-progress-track-bg, rgba(255, 255, 255, .1));border-radius:4px;overflow:hidden}.progress-bar-cell .progress-bar-fill{display:block;height:100%;background:var(--mm-progress-fill-color, #10b981);border-radius:4px;transition:width .3s ease}.progress-bar-cell .progress-bar-label{min-width:40px;text-align:right;font-size:.85em}:host-context(.k-pdf-export) .k-grid,:host-context(.k-pdf-export) .k-grid-header,:host-context(.k-pdf-export) .k-grid-content,:host-context(.k-pdf-export) .k-grid-header-wrap,:host-context(.k-pdf-export) .k-grid-content-locked,:host-context(.k-pdf-export) .k-grid-header-locked{background:#fff!important;background-color:#fff!important;background-image:none!important;color:#000!important;border-color:#000!important}:host-context(.k-pdf-export) .k-grid{border:1px solid #000!important;font-family:Arial,sans-serif!important;font-size:10px!important}:host-context(.k-pdf-export) .k-grid-header th,:host-context(.k-pdf-export) .k-header,:host-context(.k-pdf-export) .k-grid th{background:#f0f0f0!important;background-color:#f0f0f0!important;background-image:none!important;color:#000!important;border:1px solid #000!important;font-weight:700!important;padding:6px 8px!important;text-transform:none!important;letter-spacing:normal!important}:host-context(.k-pdf-export) .k-grid td,:host-context(.k-pdf-export) .k-grid-content td,:host-context(.k-pdf-export) .k-master-row td{background:#fff!important;background-color:#fff!important;background-image:none!important;color:#000!important;border:1px solid #000!important;padding:4px 8px!important}:host-context(.k-pdf-export) .k-grid tr,:host-context(.k-pdf-export) .k-master-row,:host-context(.k-pdf-export) .k-alt{background:#fff!important;background-color:#fff!important;background-image:none!important}:host-context(.k-pdf-export) .k-grid tr:hover,:host-context(.k-pdf-export) .k-grid tr.k-selected,:host-context(.k-pdf-export) .k-grid td:hover{background:#fff!important;background-color:#fff!important}:host-context(.k-pdf-export) .k-grid-toolbar,:host-context(.k-pdf-export) .k-pager,:host-context(.k-pdf-export) .k-grid-pager,:host-context(.k-pdf-export) .k-command-cell,:host-context(.k-pdf-export) .k-checkbox-column,:host-context(.k-pdf-export) kendo-grid-checkbox-column,:host-context(.k-pdf-export) .status-icons-cell,:host-context(.k-pdf-export) .status-icon,:host-context(.k-pdf-export) kendo-svg-icon{display:none!important}:host-context(.k-pdf-export) .badge-cell{background:transparent!important;border:none!important;color:inherit!important;padding:0!important;border-radius:0!important;text-transform:none!important;font-weight:inherit!important;letter-spacing:0!important;font-size:inherit!important}:host(.mm-list-view-cards) ::ng-deep .k-grid-header{display:none}:host(.mm-list-view-cards) ::ng-deep .k-grid td,:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-td{border:none;background:transparent;padding:4px 2px;white-space:normal}:host(.mm-list-view-cards) ::ng-deep .k-grid td:has(>kendo-checkbox),:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-td:has(>kendo-checkbox){padding-left:2px;padding-right:0}:host(.mm-list-view-cards) ::ng-deep .k-grid colgroup>col:first-child:not(:only-child){width:32px!important}:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-row:hover,:host(.mm-list-view-cards) ::ng-deep .k-grid tr:hover{background:transparent}:host(.mm-list-view-cards) ::ng-deep .k-grid tr.k-selected .mm-card,:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-row.k-selected .mm-card{border-color:var(--kendo-color-primary, #ff6358)}:host(.mm-list-view-cards) ::ng-deep .k-grid tr.k-selected>td,:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-row.k-selected>.k-table-td{background:transparent}.mm-card{display:flex;flex-direction:column;gap:6px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:8px;padding:10px 12px}.mm-card-header{display:flex;align-items:center;gap:8px}.mm-card-title{flex:1;min-width:0;font-weight:600;overflow-wrap:anywhere}.mm-card-actions{display:flex;align-items:center;gap:2px;flex-shrink:0}.mm-card-fields{display:flex;flex-direction:column;gap:3px}.mm-card-field{display:flex;align-items:baseline;gap:8px;font-size:.9em}.mm-card-label{flex:0 0 38%;max-width:160px;color:var(--kendo-color-subtle, #666666);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mm-card-value{flex:1;min-width:0;overflow-wrap:anywhere}::ng-deep .k-grid-toolbar .mm-toolbar-filters{display:flex;align-items:center;gap:12px;flex-wrap:wrap}::ng-deep .k-grid-toolbar .mm-toolbar-filters-separator{align-self:center;height:22px;margin:0 4px;border-left:1px solid var(--theme-border-subtle, rgba(0, 0, 0, .16));border-top:none;width:0}\n"] }]
|
|
4053
|
+
NgTemplateOutlet,
|
|
4054
|
+
ActionButtonComponent,
|
|
4055
|
+
NoRecordsTemplateDirective,
|
|
4056
|
+
EmptyStateComponent
|
|
4057
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Shared cell renderer (AB#4930): used by the regular grid columns AND the\n card-mode body lines, so both render every dataType identically. -->\n<ng-template #cellContent let-element=\"element\" let-column=\"column\">\n @if (column.formatter) {\n {{ getFormattedValue(element, column) }}\n } @else {\n @switch (column.dataType) {\n @case ('boolean') {\n <!-- Read-only marker: named after its column so a screen reader says what is checked (AB#5621). -->\n <kendo-checkbox type=\"checkbox\" [checkedState]=\"$any(getValue(element, column))\" [disabled]=\"true\"\n [inputAttributes]=\"booleanCellAttributes(column)\" />\n }\n @case ('booleanIcon') {\n <!-- Boolean as icon (AB#5623): named \"<column>: Yes|No\" like the status icons (G13). -->\n @if (booleanIconState(element, column); as state) {\n @if (booleanIcon(column, state); as icon) {\n <span\n class=\"boolean-icon\"\n [class.boolean-icon--true]=\"state === 'true'\"\n [class.boolean-icon--false]=\"state === 'false'\"\n role=\"img\"\n [title]=\"booleanIconLabel(column, state)\"\n [attr.aria-label]=\"booleanIconLabel(column, state)\"\n [style.color]=\"state === 'true' ? column.booleanIcons?.trueColor : column.booleanIcons?.falseColor\">\n <kendo-svg-icon [icon]=\"icon\"></kendo-svg-icon>\n </span>\n }\n }\n }\n @case ('iso8601') {\n {{ $any(getValue(element, column)) | date:column.format }}\n }\n @case ('bytes') {\n {{ $any(getValue(element, column)) | bytesToSize }}\n }\n @case ('statusIcons') {\n <span class=\"status-icons-cell\">\n @for (fieldConfig of getStatusFields(column); track fieldConfig.field) {\n @if (getStatusIconMapping(element, fieldConfig); as mapping) {\n <!-- Icon-only status: the mapping's tooltip is also its accessible name (AB#5621). -->\n <span\n class=\"status-icon\"\n role=\"img\"\n [title]=\"mapping.tooltip\"\n [attr.aria-label]=\"mapping.tooltip || null\"\n [style.color]=\"mapping.color\">\n <kendo-svg-icon [icon]=\"mapping.icon\"></kendo-svg-icon>\n </span>\n }\n }\n </span>\n }\n @case ('cronExpression') {\n <span class=\"cron-expression-cell\">\n <code class=\"cron-expression\">{{ getValue(element, column) }}</code>\n <span class=\"cron-description\">{{ getCronHumanReadable(getValue(element, column)) }}</span>\n </span>\n }\n @case ('numeric') {\n {{ $any(getValue(element, column)) | number:column.format }}\n }\n @case ('progressBar') {\n <span class=\"progress-bar-cell\">\n <span class=\"progress-bar-track\">\n <span class=\"progress-bar-fill\" [style.width.%]=\"getValue(element, column)\"></span>\n </span>\n <span class=\"progress-bar-label\">{{ getValue(element, column) }}%</span>\n </span>\n }\n @case ('badge') {\n @if (getBadgeMapping(element, column); as badge) {\n <span\n class=\"badge-cell\"\n [title]=\"badge.tooltip ?? ''\"\n [style.color]=\"badge.color\"\n [style.background]=\"badge.backgroundColor\"\n [style.borderColor]=\"badge.borderColor ?? badge.backgroundColor\">\n {{ badge.label ?? getValue(element, column) }}\n </span>\n } @else if (!column.badgeHideUnmapped) {\n <span class=\"badge-cell badge-cell-default\">{{ getValue(element, column) }}</span>\n }\n }\n @case ('component') {\n @if (column.cellComponent) {\n <ng-container *ngComponentOutlet=\"column.cellComponent; inputs: getCellInputs(element, column)\" />\n }\n }\n @default {\n {{ getValue(element, column) }}\n }\n }\n }\n</ng-template>\n\n<!-- Row actions (AB#4930, AB#5572): shared by the actions column and the card header. One MmAction\n per action (CommandItems via adapter): neutral icon buttons, danger colour only for destructive\n actions, max `maxInlineRowActions` slots, the rest (and the context menu) behind \"\u2026\". Disabled\n actions stay focusable and announce their reason (mm-action-button). -->\n<ng-template #rowActions let-dataItem=\"dataItem\">\n @let view = rowActionsView(dataItem);\n @if (view.inline.length > 0 || view.hasMenu) {\n <span class=\"mm-list-view-row-actions\" role=\"group\" [attr.aria-label]=\"rowActionsGroupLabel(dataItem)\">\n @for (entry of view.inline; track entry.action.id) {\n <!-- Clicks never reach the grid (no row selection / row navigation from an action). -->\n <mm-action-button [action]=\"entry.action\" [targetLabel]=\"rowLabel(dataItem)\" context=\"row\" display=\"icon\"\n (click)=\"$event.stopPropagation()\"\n (triggered)=\"onInlineRowAction(entry, dataItem)\" />\n }\n @if (view.hasMenu) {\n <button kendoButton themeColor=\"base\" fillMode=\"flat\" size=\"small\" class=\"mm-list-view-row-actions__more\"\n [svgIcon]=\"moreVerticalIcon\" data-action=\"more\"\n [title]=\"rowActionsLabel(dataItem)\" [attr.aria-label]=\"rowActionsLabel(dataItem)\" aria-haspopup=\"menu\"\n [disabled]=\"isLoading()\" (click)=\"$event.stopPropagation(); onContextMenu(dataItem, $event)\"></button>\n }\n </span>\n }\n</ng-template>\n\n<kendo-grid\n mmListViewDataBinding\n [loading]=\"isLoading()\"\n [pageSize]=\"effectivePageSize()\" [selectable]=\"selectable\" (pageChange)=\"onPageChange($event)\"\n [skip]=\"skip\" (selectionChange)=\"onRowSelect($event)\" (cellClick)=\"onCellClick($event)\"\n [pageable]=\"effectivePageable\"\n [sortable]=\"sortable\"\n [filterable]=\"_showRowFilter && !isCardMode\"\n [resizable]=\"resizable\"\n [rowClass]=\"rowClass ?? defaultRowClass\"\n>\n <kendo-grid-messages\n [pagerItemsPerPage]=\"_messages.pagerItemsPerPage\"\n [pagerOf]=\"_messages.pagerOf\"\n [pagerItems]=\"_messages.pagerItems\"\n [pagerPage]=\"_messages.pagerPage\"\n [pagerFirstPage]=\"_messages.pagerFirstPage\"\n [pagerLastPage]=\"_messages.pagerLastPage\"\n [pagerPreviousPage]=\"_messages.pagerPreviousPage\"\n [pagerNextPage]=\"_messages.pagerNextPage\"\n [noRecords]=\"_messages.noRecords\"\n ></kendo-grid-messages>\n <!-- Empty state (AB#3444): one block per list, never per row. -->\n <ng-template kendoGridNoRecordsTemplate>\n @if (emptyTemplate) {\n <ng-container *ngTemplateOutlet=\"emptyTemplate; context: emptyContext\"></ng-container>\n } @else if (emptyState) {\n @if (loadSettled() && !isLoading()) {\n @if (isFiltered) {\n <mm-empty-state class=\"mm-list-view-empty\" variant=\"no-results\"\n [heading]=\"emptyState.filteredTitle || _messages.noResultsTitle\"\n [text]=\"emptyState.filteredText || _messages.noResultsText\"></mm-empty-state>\n } @else {\n <mm-empty-state class=\"mm-list-view-empty\" variant=\"empty\"\n [heading]=\"emptyState.title\" [text]=\"emptyState.text\"></mm-empty-state>\n }\n }\n } @else {\n {{ _messages.noRecords }}\n }\n </ng-template>\n <ng-template kendoGridToolbarTemplate>\n\n @for (commandItem of leftToolbarActions; track commandItem.id) {\n @if (commandItem) {\n @switch (commandItem.type) {\n @case ('link') {\n @if (commandItem.children && commandItem.children.length > 0 && commandItem.onClick) {\n <kendo-splitbutton\n [data]=\"commandItem.children\"\n [svgIcon]=\"$any(commandItem.svgIcon)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n [textField]=\"'text'\"\n themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [buttonAttributes]=\"toolbarItemButtonAttributes(commandItem)\"\n (buttonClick)=\"onToolbarCommand(commandItem)\"\n (itemClick)=\"onToolbarDropdownItemClick($event)\">\n {{ commandItem.text }}\n </kendo-splitbutton>\n } @else if (commandItem.children && commandItem.children.length > 0) {\n <kendo-dropdownbutton\n [data]=\"commandItem.children\"\n [svgIcon]=\"$any(commandItem.svgIcon)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n [textField]=\"'text'\"\n themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [buttonAttributes]=\"toolbarItemButtonAttributes(commandItem)\"\n (itemClick)=\"onToolbarDropdownItemClick($event)\">\n {{ commandItem.text }}\n </kendo-dropdownbutton>\n } @else if (commandItem.svgIcon) {\n <button kendoTooltip kendoButton themeColor=\"primary\" [svgIcon]=\"commandItem.svgIcon\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [attr.aria-label]=\"toolbarItemAriaLabel(commandItem)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n (click)=\"onToolbarCommand(commandItem)\">{{ commandItem.text }}\n </button>\n } @else {\n <button kendoTooltip kendoButton themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [attr.aria-label]=\"toolbarItemAriaLabel(commandItem)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n (click)=\"onToolbarCommand(commandItem)\">{{ commandItem.text }}\n </button>\n }\n }\n @case ('separator'){\n <kendo-separator></kendo-separator>\n }\n }\n }\n }\n\n <!-- Host scope/filter controls (view switches and the like). They steer the\n table, so they sit in the table's own band instead of a second strip\n stacked above the grid. -->\n @if (filtersTemplate) {\n <!-- A rule between the host's actions and its filters: both sit on the\n left of the same band, but one acts on records and the other changes\n what the list shows. Only drawn when actions precede them. -->\n @if (leftToolbarActions.length > 0) {\n <kendo-separator class=\"mm-toolbar-filters-separator\"></kendo-separator>\n }\n <div class=\"mm-toolbar-filters\">\n <ng-container [ngTemplateOutlet]=\"filtersTemplate.templateRef\"></ng-container>\n </div>\n }\n\n <kendo-grid-spacer></kendo-grid-spacer>\n @if (searchTextBoxEnabled) {\n <input\n class=\"k-textbox k-input k-input-md k-rounded-md mm-search-input\"\n [placeholder]=\"_messages.searchPlaceholder\"\n [value]=\"searchValue\"\n (input)=\"onFilter($any($event.target).value || null)\"\n />\n }\n\n @for (commandItem of rightToolbarActions; track commandItem.id) {\n @if (commandItem) {\n @switch (commandItem.type) {\n @case ('link') {\n @if (commandItem.children && commandItem.children.length > 0 && commandItem.onClick) {\n <kendo-splitbutton\n [data]=\"commandItem.children\"\n [svgIcon]=\"$any(commandItem.svgIcon)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n [textField]=\"'text'\"\n themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [buttonAttributes]=\"toolbarItemButtonAttributes(commandItem)\"\n (buttonClick)=\"onToolbarCommand(commandItem)\"\n (itemClick)=\"onToolbarDropdownItemClick($event)\">\n {{ commandItem.text }}\n </kendo-splitbutton>\n } @else if (commandItem.children && commandItem.children.length > 0) {\n <kendo-dropdownbutton\n [data]=\"commandItem.children\"\n [svgIcon]=\"$any(commandItem.svgIcon)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n [textField]=\"'text'\"\n themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [buttonAttributes]=\"toolbarItemButtonAttributes(commandItem)\"\n (itemClick)=\"onToolbarDropdownItemClick($event)\">\n {{ commandItem.text }}\n </kendo-dropdownbutton>\n } @else if (commandItem.svgIcon) {\n <button kendoTooltip kendoButton themeColor=\"primary\" [svgIcon]=\"commandItem.svgIcon\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [attr.aria-label]=\"toolbarItemAriaLabel(commandItem)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n (click)=\"onToolbarCommand(commandItem)\">{{ commandItem.text }}\n </button>\n } @else {\n <button kendoTooltip kendoButton themeColor=\"primary\"\n [fillMode]=\"$any(commandItem.fillMode ?? 'solid')\"\n [title]=\"commandItem.tooltip ?? commandItem.text\"\n [attr.aria-label]=\"toolbarItemAriaLabel(commandItem)\"\n [disabled]=\"getToolbarItemDisabled(commandItem) || isLoading()\"\n (click)=\"onToolbarCommand(commandItem)\">{{ commandItem.text }}\n </button>\n }\n }\n @case ('separator'){\n <kendo-separator></kendo-separator>\n }\n }\n }\n }\n\n <!-- Commands: laid out while there is room, collapsed into one menu once\n the list gets narrow. Same behaviour in every host app \u2014 a list this\n wide has space for five buttons, and a list this narrow does not. -->\n @if (commandsCollapsed) {\n <kendo-dropdownbutton class=\"mm-toolbar-commands\" [data]=\"toolbarCommands\" textField=\"text\" [svgIcon]=\"commandsIcon\"\n fillMode=\"flat\" themeColor=\"primary\" [disabled]=\"isLoading()\"\n [title]=\"_messages.commands\"\n [buttonAttributes]=\"commandsMenuButtonAttributes\"\n (itemClick)=\"onCommand($any($event).id)\"></kendo-dropdownbutton>\n } @else {\n @for (command of toolbarCommands; track command.id) {\n @switch (command.id) {\n @case ('excel') {\n <button kendoTooltip kendoGridExcelCommand themeColor=\"primary\" fillMode=\"flat\"\n [svgIcon]=\"command.svgIcon\" [disabled]=\"isLoading()\" [title]=\"command.text\"\n [attr.aria-label]=\"command.text\" [attr.data-command]=\"command.id\"></button>\n }\n @case ('pdf') {\n <button kendoTooltip kendoGridPDFCommand themeColor=\"primary\" fillMode=\"flat\"\n [svgIcon]=\"command.svgIcon\" [disabled]=\"isLoading()\" [title]=\"command.text\"\n [attr.aria-label]=\"command.text\" [attr.data-command]=\"command.id\"></button>\n }\n @default {\n <!-- The row filter is a toggle: its on state is shown (k-selected) and announced. -->\n <button kendoTooltip kendoButton themeColor=\"primary\" fillMode=\"flat\"\n [svgIcon]=\"command.svgIcon\" (click)=\"onCommand(command.id)\"\n [class.k-selected]=\"command.id === 'rowFilter' && _showRowFilter\"\n [attr.aria-pressed]=\"command.id === 'rowFilter' ? _showRowFilter : null\"\n [attr.data-command]=\"command.id\"\n [disabled]=\"isLoading()\" [title]=\"command.text\" [attr.aria-label]=\"command.text\"></button>\n }\n }\n }\n }\n </ng-template>\n\n @if (showCheckboxColumn()) {\n <kendo-grid-checkbox-column [showSelectAll]=\"showRowSelectAllCheckBox\"\n [width]=\"effectiveCheckboxColumnWidth\"></kendo-grid-checkbox-column>\n }\n\n @if (!isCardMode) {\n @for (column of columns; track column.field) {\n <kendo-grid-column [field]=\"column.field\"\n [filter]=\"getFilterType(column)\" format=\"{{column.format}}\"\n [width]=\"$any(getEffectiveWidth(column))\"\n [minResizableWidth]=\"column.minWidth ?? 10\"\n [hidden]=\"isColumnHidden(column)\"\n [sortable]=\"column.sortable !== false\"\n [filterable]=\"column.filterable !== false\"\n [class]=\"alignClass(column)\"\n [headerClass]=\"alignClass(column)\"\n [title]=\"getDisplayName(column) | pascalCase\">\n <ng-template kendoGridFilterCellTemplate let-filter let-gridColumn=\"column\">\n @if (hasFilterOptions(column)) {\n <kendo-dropdownlist\n class=\"status-filter-dropdown\"\n [data]=\"column.filterOptions!\"\n textField=\"text\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n [value]=\"getSelectedFilterValue(column)\"\n [defaultItem]=\"{ text: '', value: null }\"\n [popupSettings]=\"{ width: 'auto' }\"\n (valueChange)=\"onDropdownFilter($event, column)\"\n >\n <ng-template kendoDropDownListValueTemplate let-dataItem>\n @if (dataItem?.value && column.statusMapping?.[dataItem.value]; as mapping) {\n <span class=\"filter-status-item\">\n <span role=\"img\" [attr.aria-label]=\"dataItem.text || mapping.tooltip || null\" [title]=\"dataItem.text || mapping.tooltip\"\n [style.color]=\"mapping.color\">\n <kendo-svg-icon [icon]=\"mapping.icon\" [size]=\"'small'\"></kendo-svg-icon>\n </span>\n </span>\n } @else if (dataItem?.text) {\n <span>{{ dataItem.text }}</span>\n }\n </ng-template>\n <ng-template kendoDropDownListItemTemplate let-dataItem>\n @if (dataItem?.value && column.statusMapping?.[dataItem.value]; as mapping) {\n <span style=\"display: inline-flex; align-items: center; gap: 8px;\">\n <span [style.color]=\"mapping.color\">\n <kendo-svg-icon [icon]=\"mapping.icon\" [size]=\"'small'\"></kendo-svg-icon>\n </span>\n <span>{{ dataItem.text }}</span>\n </span>\n } @else {\n <span>{{ dataItem.text }}</span>\n }\n </ng-template>\n </kendo-dropdownlist>\n } @else if (column.dataType === 'numericRange') {\n <span class=\"numeric-range-filter\">\n <kendo-numerictextbox\n [value]=\"$any(getRangeFilterValue(column, 'gte'))\"\n (valueChange)=\"onRangeFilterChange($event, column, 'gte')\"\n [spinners]=\"false\"\n [decimals]=\"2\"\n placeholder=\"From\"\n size=\"small\"\n ></kendo-numerictextbox>\n <span class=\"range-separator\">\u2013</span>\n <kendo-numerictextbox\n [value]=\"$any(getRangeFilterValue(column, 'lte'))\"\n (valueChange)=\"onRangeFilterChange($event, column, 'lte')\"\n [spinners]=\"false\"\n [decimals]=\"2\"\n placeholder=\"To\"\n size=\"small\"\n ></kendo-numerictextbox>\n </span>\n } @else {\n @switch (getFilterType(column)) {\n @case ('numeric') {\n <kendo-grid-numeric-filter-cell [column]=\"gridColumn\" [filter]=\"filter\" [showOperators]=\"true\" [operator]=\"column.filterOperator || 'eq'\"\n [spinners]=\"!isCompactFilter(column)\" [class.mm-filter-compact]=\"isCompactFilter(column)\"></kendo-grid-numeric-filter-cell>\n }\n @case ('boolean') {\n <kendo-grid-boolean-filter-cell [column]=\"gridColumn\" [filter]=\"filter\"></kendo-grid-boolean-filter-cell>\n }\n @case ('date') {\n <kendo-grid-date-filter-cell [column]=\"gridColumn\" [filter]=\"filter\" [showOperators]=\"true\" [operator]=\"column.filterOperator || 'eq'\"\n [class.mm-filter-compact]=\"isCompactFilter(column)\"></kendo-grid-date-filter-cell>\n }\n @default {\n <kendo-grid-string-filter-cell [column]=\"gridColumn\" [filter]=\"filter\" [showOperators]=\"false\"></kendo-grid-string-filter-cell>\n }\n }\n }\n </ng-template>\n <ng-template kendoGridCellTemplate let-dataItem>\n <span\n [class.mm-cell-truncate]=\"column.truncate\"\n [attr.title]=\"column.truncate ? getCellTitle(dataItem, column) : null\">\n <ng-container\n [ngTemplateOutlet]=\"cellContent\"\n [ngTemplateOutletContext]=\"{ element: dataItem, column: column }\"\n ></ng-container>\n </span>\n </ng-template>\n </kendo-grid-column>\n }\n\n @if (hasRowActions) {\n <kendo-grid-command-column [title]=\"_messages.actionsColumnTitle\" [width]=\"effectiveActionsColumnWidth\">\n <ng-template kendoGridCellTemplate let-dataItem>\n <ng-container\n [ngTemplateOutlet]=\"rowActions\"\n [ngTemplateOutletContext]=\"{ dataItem: dataItem }\"\n ></ng-container>\n </ng-template>\n </kendo-grid-command-column>\n }\n } @else {\n <!-- Card mode (AB#4930): one full-width column renders the whole row as a\n stacked card \u2014 title from the first column, labeled lines for the\n rest (empty values skipped), row actions in the card header. -->\n <kendo-grid-column [sortable]=\"false\" [filterable]=\"false\" title=\"\">\n <ng-template kendoGridCellTemplate let-dataItem>\n <div class=\"mm-card\">\n <div class=\"mm-card-header\">\n @if (cardTitleColumn; as titleColumn) {\n <span class=\"mm-card-title\">\n <ng-container\n [ngTemplateOutlet]=\"cellContent\"\n [ngTemplateOutletContext]=\"{ element: dataItem, column: titleColumn }\"\n ></ng-container>\n </span>\n }\n <span class=\"mm-card-actions\">\n <ng-container\n [ngTemplateOutlet]=\"rowActions\"\n [ngTemplateOutletContext]=\"{ dataItem: dataItem }\"\n ></ng-container>\n </span>\n </div>\n <div class=\"mm-card-fields\">\n @for (column of cardBodyColumns; track column.field) {\n @if (hasCardValue(dataItem, column)) {\n <div class=\"mm-card-field\">\n <span class=\"mm-card-label\">{{ getDisplayName(column) | pascalCase }}</span>\n <span class=\"mm-card-value\">\n <ng-container\n [ngTemplateOutlet]=\"cellContent\"\n [ngTemplateOutletContext]=\"{ element: dataItem, column: column }\"\n ></ng-container>\n </span>\n </div>\n }\n }\n </div>\n </div>\n </ng-template>\n </kendo-grid-column>\n }\n\n <kendo-grid-excel fileName=\"{{excelExportFileName}}\">\n @for (column of columns; track column.field) {\n <kendo-excelexport-column [field]=\"column.field\"\n [title]=\"getDisplayName(column) | pascalCase\">\n </kendo-excelexport-column>\n }\n </kendo-grid-excel>\n\n <kendo-grid-pdf fileName=\"{{pdfExportFileName}}\" paperSize=\"A4\" [repeatHeaders]=\"true\"\n [landscape]=\"true\">\n @for (column of columns; track column.field) {\n <kendo-grid-column [field]=\"column.field\"\n [filter]=\"getFilterType(column)\"\n [title]=\"getDisplayName(column) | pascalCase\"></kendo-grid-column>\n }\n <kendo-grid-pdf-margin top=\"1.5cm\" left=\"1cm\" right=\"1cm\" bottom=\"1.5cm\"></kendo-grid-pdf-margin>\n <ng-template kendoGridPDFTemplate let-pageNum=\"pageNum\" let-totalPages=\"totalPages\">\n <div class=\"page-template\">\n <div class=\"footer\" style=\"position: absolute; bottom: 0; width: 100%; text-align: center; font-size: 9px; color: #666;\">\n {{ getPdfPageText(pageNum, totalPages) }}\n </div>\n </div>\n </ng-template>\n </kendo-grid-pdf>\n</kendo-grid>\n\n<kendo-contextmenu\n #gridmenu\n [kendoMenuHierarchyBinding]=\"_contextMenuItems\"\n [textField]=\"['text']\"\n childrenField=\"items\"\n svgIconField=\"svgIcon\"\n separatorField=\"separator\"\n disabledField=\"disabled\"\n cssClassField=\"cssClass\"\n (popupClose)=\"onContextMenuClosed($event)\"\n (select)=\"onContextMenuSelect($event)\"\n></kendo-contextmenu>\n", styles: [":host{display:flex;flex-direction:column;min-height:0}:host>kendo-grid{flex:1;min-height:0}.mm-search-input{width:165px}:host ::ng-deep .k-grid th:has(input[type=checkbox]),:host ::ng-deep .k-grid td:has(>kendo-checkbox){text-overflow:clip}:host ::ng-deep .k-command-cell{white-space:nowrap;overflow:hidden;vertical-align:middle}:host ::ng-deep .k-command-cell .k-button{display:inline-flex;vertical-align:middle;margin:0}:host ::ng-deep .k-command-cell .k-button+.k-button{margin-left:4px}:host(.mm-list-view-narrow) ::ng-deep .k-grid-toolbar{flex-wrap:wrap;row-gap:6px}:host(.mm-list-view-narrow) ::ng-deep .k-grid-toolbar .mm-search-input{width:100%;order:10}:host(.mm-list-view-narrow) ::ng-deep .k-grid-toolbar kendo-grid-spacer{display:none}:host(.mm-list-view-narrow) ::ng-deep .k-grid-toolbar .mm-toolbar-commands{margin-left:auto}:host ::ng-deep .k-grid td.mm-col-align-right{text-align:right;font-variant-numeric:tabular-nums}:host ::ng-deep .k-grid td.mm-col-align-center{text-align:center}:host ::ng-deep .k-grid th.mm-col-align-right .k-cell-inner>.k-link{justify-content:flex-end}:host ::ng-deep .k-grid th.mm-col-align-center .k-cell-inner>.k-link{justify-content:center}:host ::ng-deep .k-filter-row .mm-filter-compact .k-filtercell-wrapper{gap:2px}:host ::ng-deep .k-filter-row .mm-filter-compact .k-datepicker>.k-input-button{display:none}:host ::ng-deep .k-filter-row .mm-filter-compact .k-input-inner{padding-left:4px;padding-right:4px;font-size:12px}:host ::ng-deep .k-filter-row .mm-filter-compact .k-filtercell-operator .k-dropdown-operator{width:26px;min-width:0}:host ::ng-deep .k-filter-row .mm-filter-compact .k-filtercell-operator .k-dropdown-operator .k-input-button{padding:2px}.mm-cell-truncate{display:block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.status-icons-cell{display:inline-flex;align-items:center;gap:8px}.status-icons-cell .status-icon{display:inline-flex;align-items:center;justify-content:center;cursor:default}.status-icons-cell .status-icon kendo-svg-icon{width:18px;height:18px}:host ::ng-deep .filter-status-item{display:inline-flex;align-items:center;gap:10px}:host ::ng-deep .status-filter-dropdown .k-input-value-text{font-size:0}:host ::ng-deep .status-filter-dropdown .k-input-value-text .filter-status-item,:host ::ng-deep .status-filter-dropdown .k-input-value-text>span{font-size:14px}.numeric-range-filter{display:flex;align-items:center;gap:4px}.numeric-range-filter kendo-numerictextbox{flex:1;min-width:0}.numeric-range-filter .range-separator{flex-shrink:0;opacity:.6}.cron-expression-cell{display:flex;flex-direction:column;gap:2px}.cron-expression-cell .cron-expression{font-family:Roboto Mono,Consolas,monospace;font-size:.85em;background:var(--mm-cron-code-bg, #eeeeee);color:var(--mm-cron-code-text, #333333);padding:2px 6px;border-radius:3px}.cron-expression-cell .cron-description{font-size:.8em;color:var(--mm-cron-text-secondary, #666666)}.badge-cell{display:inline-block;padding:2px 10px;font-size:.75rem;font-weight:600;border-radius:12px;border:1px solid var(--mm-badge-default-border, transparent);background:var(--mm-badge-default-bg, rgba(127, 127, 127, .15));color:var(--mm-badge-default-color, inherit);white-space:nowrap;line-height:1.4}.badge-cell-default{opacity:.7;font-style:italic}.progress-bar-cell{display:inline-flex;align-items:center;gap:8px;width:100%}.progress-bar-cell .progress-bar-track{flex:1;height:8px;background:var(--mm-progress-track-bg, rgba(255, 255, 255, .1));border-radius:4px;overflow:hidden}.progress-bar-cell .progress-bar-fill{display:block;height:100%;background:var(--mm-progress-fill-color, #10b981);border-radius:4px;transition:width .3s ease}.progress-bar-cell .progress-bar-label{min-width:40px;text-align:right;font-size:.85em}:host-context(.k-pdf-export) .k-grid,:host-context(.k-pdf-export) .k-grid-header,:host-context(.k-pdf-export) .k-grid-content,:host-context(.k-pdf-export) .k-grid-header-wrap,:host-context(.k-pdf-export) .k-grid-content-locked,:host-context(.k-pdf-export) .k-grid-header-locked{background:#fff!important;background-color:#fff!important;background-image:none!important;color:#000!important;border-color:#000!important}:host-context(.k-pdf-export) .k-grid{border:1px solid #000!important;font-family:Arial,sans-serif!important;font-size:10px!important}:host-context(.k-pdf-export) .k-grid-header th,:host-context(.k-pdf-export) .k-header,:host-context(.k-pdf-export) .k-grid th{background:#f0f0f0!important;background-color:#f0f0f0!important;background-image:none!important;color:#000!important;border:1px solid #000!important;font-weight:700!important;padding:6px 8px!important;text-transform:none!important;letter-spacing:normal!important}:host-context(.k-pdf-export) .k-grid td,:host-context(.k-pdf-export) .k-grid-content td,:host-context(.k-pdf-export) .k-master-row td{background:#fff!important;background-color:#fff!important;background-image:none!important;color:#000!important;border:1px solid #000!important;padding:4px 8px!important}:host-context(.k-pdf-export) .k-grid tr,:host-context(.k-pdf-export) .k-master-row,:host-context(.k-pdf-export) .k-alt{background:#fff!important;background-color:#fff!important;background-image:none!important}:host-context(.k-pdf-export) .k-grid tr:hover,:host-context(.k-pdf-export) .k-grid tr.k-selected,:host-context(.k-pdf-export) .k-grid td:hover{background:#fff!important;background-color:#fff!important}:host-context(.k-pdf-export) .k-grid-toolbar,:host-context(.k-pdf-export) .k-pager,:host-context(.k-pdf-export) .k-grid-pager,:host-context(.k-pdf-export) .k-command-cell,:host-context(.k-pdf-export) .k-checkbox-column,:host-context(.k-pdf-export) kendo-grid-checkbox-column,:host-context(.k-pdf-export) .status-icons-cell,:host-context(.k-pdf-export) .status-icon,:host-context(.k-pdf-export) kendo-svg-icon{display:none!important}:host-context(.k-pdf-export) .badge-cell{background:transparent!important;border:none!important;color:inherit!important;padding:0!important;border-radius:0!important;text-transform:none!important;font-weight:inherit!important;font-size:inherit!important}:host(.mm-list-view-cards) ::ng-deep .k-grid-header{display:none}:host(.mm-list-view-cards) ::ng-deep .k-grid td,:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-td{border:none;background:transparent;padding:4px 2px;white-space:normal}:host(.mm-list-view-cards) ::ng-deep .k-grid td:has(>kendo-checkbox),:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-td:has(>kendo-checkbox){padding-left:2px;padding-right:0}:host(.mm-list-view-cards) ::ng-deep .k-grid colgroup>col:first-child:not(:only-child){width:32px!important}:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-row:hover,:host(.mm-list-view-cards) ::ng-deep .k-grid tr:hover{background:transparent}:host(.mm-list-view-cards) ::ng-deep .k-grid tr.k-selected .mm-card,:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-row.k-selected .mm-card{border-color:var(--kendo-color-primary, #ff6358)}:host(.mm-list-view-cards) ::ng-deep .k-grid tr.k-selected>td,:host(.mm-list-view-cards) ::ng-deep .k-grid .k-table-row.k-selected>.k-table-td{background:transparent}.mm-card{display:flex;flex-direction:column;gap:6px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:8px;padding:10px 12px}.mm-card-header{display:flex;align-items:center;gap:8px}.mm-card-title{flex:1;min-width:0;font-weight:600;overflow-wrap:anywhere}.mm-card-actions{display:flex;align-items:center;gap:2px;flex-shrink:0}.mm-card-fields{display:flex;flex-direction:column;gap:3px}.mm-card-field{display:flex;align-items:baseline;gap:8px;font-size:.9em}.mm-card-label{flex:0 0 38%;max-width:160px;color:var(--kendo-color-subtle, #666666);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mm-card-value{flex:1;min-width:0;overflow-wrap:anywhere}::ng-deep .k-grid-toolbar .mm-toolbar-filters{display:flex;align-items:center;gap:12px;flex-wrap:wrap}::ng-deep .k-grid-toolbar .mm-toolbar-filters-separator{align-self:center;height:22px;margin:0 4px;border-left:1px solid var(--theme-border-subtle, rgba(0, 0, 0, .16));border-top:none;width:0}::ng-deep .k-menu-group .mm-list-view-menu-item--danger>.k-link,::ng-deep .k-menu-group .mm-list-view-menu-item--danger>.k-menu-link{color:var(--theme-status-error, #c0385f)}.mm-list-view-row-actions{display:inline-flex;align-items:center;gap:4px;white-space:nowrap;vertical-align:middle}.mm-list-view-row-actions .mm-list-view-row-actions__more:focus-visible{outline:none;box-shadow:var(--theme-focus-ring, 0 0 0 2px #ffffff, 0 0 0 4px #2e8473)}.boolean-icon{display:inline-flex;align-items:center}.boolean-icon.boolean-icon--true{color:var(--mm-list-boolean-true-color, var(--kendo-color-success, #2fb37a))}.boolean-icon.boolean-icon--false{color:var(--mm-list-boolean-false-color, var(--kendo-color-subtle, #8a94a0))}\n"] }]
|
|
2899
4058
|
}], ctorParameters: () => [], propDecorators: { filtersTemplate: [{
|
|
2900
4059
|
type: ContentChild,
|
|
2901
4060
|
args: [ListViewFiltersDirective]
|
|
@@ -2932,6 +4091,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
2932
4091
|
type: Input
|
|
2933
4092
|
}], contextMenuCommandItems: [{
|
|
2934
4093
|
type: Input
|
|
4094
|
+
}], rowActions: [{
|
|
4095
|
+
type: Input
|
|
4096
|
+
}], maxInlineRowActions: [{
|
|
4097
|
+
type: Input
|
|
4098
|
+
}], rowAction: [{
|
|
4099
|
+
type: Output
|
|
2935
4100
|
}], excelExportFileName: [{
|
|
2936
4101
|
type: Input
|
|
2937
4102
|
}], pdfExportFileName: [{
|
|
@@ -2961,6 +4126,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
2961
4126
|
type: Input
|
|
2962
4127
|
}], collapseCommandsBelow: [{
|
|
2963
4128
|
type: Input
|
|
4129
|
+
}], hasExternalFilters: [{
|
|
4130
|
+
type: Input
|
|
4131
|
+
}], emptyState: [{
|
|
4132
|
+
type: Input
|
|
4133
|
+
}], emptyTemplate: [{
|
|
4134
|
+
type: Input
|
|
2964
4135
|
}], isCardMode: [{
|
|
2965
4136
|
type: HostBinding,
|
|
2966
4137
|
args: ['class.mm-list-view-cards']
|
|
@@ -2974,6 +4145,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
2974
4145
|
type: Input
|
|
2975
4146
|
}], resetFilters: [{
|
|
2976
4147
|
type: Output
|
|
4148
|
+
}], rowLabelField: [{
|
|
4149
|
+
type: Input
|
|
2977
4150
|
}] } });
|
|
2978
4151
|
|
|
2979
4152
|
class TreeComponent {
|
|
@@ -3344,14 +4517,14 @@ class BaseFormComponent {
|
|
|
3344
4517
|
return this.form?.dirty ?? false;
|
|
3345
4518
|
}
|
|
3346
4519
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: BaseFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3347
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.5", type: BaseFormComponent, isStandalone: true, selector: "mm-base-form", inputs: { form: "form", config: "config" }, outputs: { saveForm: "saveForm", cancelForm: "cancelForm" }, providers: [{ provide: HAS_UNSAVED_CHANGES, useExisting: BaseFormComponent }], queries: [{ propertyName: "titleExtra", first: true, predicate: FormTitleExtraDirective, descendants: true }], hostDirectives: [{ directive: UnsavedChangesDirective }], ngImport: i0, template: "<div class=\"mm-base-form-container\" [class.with-card]=\"config.showCard\" [class.without-card]=\"!config.showCard\">\n <!-- Title (no card mode) -->\n <div *ngIf=\"!config.showCard && (title || titleExtra)\" class=\"mm-form-title-row mm-form-title-standalone\">\n <h2 *ngIf=\"title\" class=\"mm-form-title\">{{ title }}</h2>\n <ng-container *ngIf=\"titleExtra\" [ngTemplateOutlet]=\"titleExtra.templateRef\"></ng-container>\n <span *ngIf=\"config.hasChanges\" class=\"mm-modified-badge\">{{ unsavedChangesText }}</span>\n </div>\n\n <!-- Main form wrapper - styled as card or plain based on CSS class -->\n <div class=\"mm-form-wrapper\" [style.maxWidth]=\"config.showCard ? (config.cardWidth || '800px') : null\">\n <!-- Card header (only in card mode) -->\n <div *ngIf=\"config.showCard && (title || titleExtra)\" class=\"mm-form-header\">\n <h2 *ngIf=\"title\" class=\"mm-form-title\">{{ title }}</h2>\n <ng-container *ngIf=\"titleExtra\" [ngTemplateOutlet]=\"titleExtra.templateRef\"></ng-container>\n <span *ngIf=\"config.hasChanges\" class=\"mm-modified-badge\">{{ unsavedChangesText }}</span>\n </div>\n\n <!-- Form body with content projection -->\n <div class=\"mm-form-body\">\n <div *ngIf=\"config.isLoading && config.showLoadingOverlay\" class=\"mm-loading-overlay\">\n <div class=\"k-loader k-loader-lg\"></div>\n <p>{{ loadingText }}</p>\n </div>\n <ng-content></ng-content>\n </div>\n\n <!-- Form actions -->\n <div class=\"mm-form-actions\">\n <!-- Status indicator (left side) -->\n <div class=\"mm-form-status\">\n <span class=\"mm-status-indicator\" [class.modified]=\"config.hasChanges\">\n <span class=\"mm-status-dot\"></span>\n <span class=\"mm-status-text\">{{ config.hasChanges ? modifiedText : readyText }}</span>\n </span>\n </div>\n\n <!-- Action buttons (right side) -->\n <div class=\"mm-form-buttons\">\n <button\n *ngIf=\"showSaveButton\"\n kendoButton\n type=\"submit\"\n [svgIcon]=\"saveIcon\"\n themeColor=\"primary\"\n [disabled]=\"saveButtonDisabled\"\n (click)=\"onSave()\">\n {{ saveButtonText }}\n </button>\n <button\n *ngIf=\"showCancelButton\"\n kendoButton\n type=\"button\"\n [svgIcon]=\"cancelIcon\"\n [disabled]=\"!!config.isSaving\"\n (click)=\"onCancel()\">\n {{ cancelButtonText }}\n </button>\n <ng-content select=\"[additionalActions]\"></ng-content>\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{--mm-form-bg: var(--kendo-color-surface);--mm-form-bg-alt: var(--kendo-color-surface-alt);--mm-form-bg-elevated: var(--kendo-color-surface-alt);--mm-form-border: var(--kendo-color-border);--mm-form-border-subtle: var(--kendo-color-border-alt, var(--mm-form-border));--mm-form-text: var(--kendo-color-on-app-surface);--mm-form-text-subtle: var(--kendo-color-subtle);--mm-form-text-label: var(--kendo-color-on-app-surface);--mm-form-accent: var(--kendo-color-primary);--mm-form-error: var(--kendo-color-error);--mm-form-status-ready: #37b400;--mm-form-status-modified: #da9162;--mm-form-padding: 1.5rem;--mm-form-padding-sm: 1rem;--mm-form-gap: .5rem;--mm-form-radius: 4px;--mm-form-shadow: 0 2px 6px rgba(0, 0, 0, .15);--mm-form-overlay-bg: var(--kendo-color-surface)}.mm-base-form-container{padding:var(--mm-form-padding);height:100%;overflow:auto}.mm-base-form-container .mm-form-wrapper{margin:0 auto}.mm-base-form-container.with-card .mm-form-wrapper{background:var(--mm-form-bg);border:1px solid var(--mm-form-border);border-radius:var(--mm-form-radius);box-shadow:var(--mm-form-shadow)}.mm-base-form-container.with-card .mm-form-header{display:flex;align-items:center;gap:1rem;padding:var(--mm-form-padding-sm) var(--mm-form-padding);border-bottom:1px solid var(--mm-form-border);background:var(--mm-form-bg-alt)}.mm-base-form-container.with-card .mm-form-body{padding:var(--mm-form-padding)}.mm-base-form-container.with-card .mm-form-actions{padding:var(--mm-form-padding-sm) var(--mm-form-padding);border-top:1px solid var(--mm-form-border);background:var(--mm-form-bg-alt)}.mm-base-form-container.without-card .mm-form-wrapper{max-width:1200px}.mm-base-form-container.without-card .mm-form-title-standalone{margin-bottom:var(--mm-form-padding)}.mm-base-form-container.without-card .mm-form-actions{margin-top:var(--mm-form-padding);padding-top:var(--mm-form-padding);border-top:1px solid var(--mm-form-border)}.mm-base-form-container .mm-form-title-row{display:flex;align-items:center;gap:1rem}.mm-base-form-container .mm-form-title{font-size:1.5rem;font-weight:500;margin:0;color:var(--mm-form-text)}.mm-base-form-container .mm-modified-badge{display:inline-flex;align-items:center;padding:.25rem .75rem;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--mm-form-status-modified);background:#da916226;border:1px solid rgba(218,145,98,.4);border-radius:var(--mm-form-radius);animation:pulse-badge 2s ease-in-out infinite}@keyframes pulse-badge{0%,to{opacity:1}50%{opacity:.7}}.mm-base-form-container .mm-form-body{position:relative;min-height:100px}.mm-base-form-container .mm-form-actions{display:flex;gap:var(--mm-form-gap);justify-content:space-between;align-items:center}.mm-base-form-container .mm-form-status{display:flex;align-items:center}.mm-base-form-container .mm-status-indicator{display:flex;align-items:center;gap:.5rem;padding:.25rem .75rem;background:#37b4001a;border:1px solid rgba(55,180,0,.3);border-radius:var(--mm-form-radius)}.mm-base-form-container .mm-status-indicator .mm-status-dot{width:8px;height:8px;border-radius:50%;background:var(--mm-form-status-ready);box-shadow:0 0 8px #37b40099;animation:pulse-dot 2s ease-in-out infinite}.mm-base-form-container .mm-status-indicator .mm-status-text{font-size:.7rem;font-weight:600;letter-spacing:1px;color:var(--mm-form-status-ready);text-transform:uppercase}.mm-base-form-container .mm-status-indicator.modified{background:#da91621a;border-color:#da91624d}.mm-base-form-container .mm-status-indicator.modified .mm-status-dot{background:var(--mm-form-status-modified);box-shadow:0 0 8px #da916299}.mm-base-form-container .mm-status-indicator.modified .mm-status-text{color:var(--mm-form-status-modified)}@keyframes pulse-dot{0%,to{opacity:1}50%{opacity:.6}}.mm-base-form-container .mm-form-buttons{display:flex;gap:var(--mm-form-gap)}.mm-base-form-container .mm-form-buttons button{min-width:100px}.mm-base-form-container .mm-loading-overlay{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;background-color:var(--mm-form-overlay-bg);opacity:.95;z-index:10}.mm-base-form-container .mm-loading-overlay p{margin-top:1rem;color:var(--mm-form-text-subtle);font-size:1rem}::ng-deep .mm-base-form-container fieldset[kendoformfieldset],::ng-deep .mm-base-form-container .k-form-fieldset{border:1px solid var(--mm-form-border);border-radius:var(--mm-form-radius);padding:var(--mm-form-padding);margin:0;background:var(--mm-form-bg-alt)}::ng-deep .mm-base-form-container fieldset[kendoformfieldset] legend,::ng-deep .mm-base-form-container fieldset[kendoformfieldset] .k-form-legend,::ng-deep .mm-base-form-container .k-form-fieldset legend,::ng-deep .mm-base-form-container .k-form-fieldset .k-form-legend{font-size:1.125rem;font-weight:500;color:var(--mm-form-accent);padding:0 .5rem}::ng-deep .mm-base-form-container kendo-formfield kendo-label,::ng-deep .mm-base-form-container kendo-formfield .k-label,::ng-deep .mm-base-form-container .k-form-field kendo-label,::ng-deep .mm-base-form-container .k-form-field .k-label{font-weight:500;color:var(--mm-form-text-label);margin-bottom:.25rem}::ng-deep .mm-base-form-container kendo-formfield kendo-label .k-required,::ng-deep .mm-base-form-container kendo-formfield .k-label .k-required,::ng-deep .mm-base-form-container .k-form-field kendo-label .k-required,::ng-deep .mm-base-form-container .k-form-field .k-label .k-required{color:var(--mm-form-error)}::ng-deep .mm-base-form-container kendo-formfield kendo-textbox,::ng-deep .mm-base-form-container kendo-formfield kendo-numerictextbox,::ng-deep .mm-base-form-container kendo-formfield kendo-textarea,::ng-deep .mm-base-form-container kendo-formfield kendo-datepicker,::ng-deep .mm-base-form-container kendo-formfield kendo-datetimepicker,::ng-deep .mm-base-form-container kendo-formfield kendo-timepicker,::ng-deep .mm-base-form-container kendo-formfield kendo-dropdownlist,::ng-deep .mm-base-form-container kendo-formfield kendo-combobox,::ng-deep .mm-base-form-container kendo-formfield kendo-multiselect,::ng-deep .mm-base-form-container kendo-formfield kendo-autocomplete,::ng-deep .mm-base-form-container .k-form-field kendo-textbox,::ng-deep .mm-base-form-container .k-form-field kendo-numerictextbox,::ng-deep .mm-base-form-container .k-form-field kendo-textarea,::ng-deep .mm-base-form-container .k-form-field kendo-datepicker,::ng-deep .mm-base-form-container .k-form-field kendo-datetimepicker,::ng-deep .mm-base-form-container .k-form-field kendo-timepicker,::ng-deep .mm-base-form-container .k-form-field kendo-dropdownlist,::ng-deep .mm-base-form-container .k-form-field kendo-combobox,::ng-deep .mm-base-form-container .k-form-field kendo-multiselect,::ng-deep .mm-base-form-container .k-form-field kendo-autocomplete{width:100%}::ng-deep .mm-base-form-container kendo-formfield kendo-formhint,::ng-deep .mm-base-form-container kendo-formfield .k-form-hint,::ng-deep .mm-base-form-container .k-form-field kendo-formhint,::ng-deep .mm-base-form-container .k-form-field .k-form-hint{color:var(--mm-form-text-subtle);font-size:.875rem}::ng-deep .mm-base-form-container kendo-formfield kendo-formerror,::ng-deep .mm-base-form-container kendo-formfield .k-form-error,::ng-deep .mm-base-form-container .k-form-field kendo-formerror,::ng-deep .mm-base-form-container .k-form-field .k-form-error{color:var(--mm-form-error);font-size:.875rem}::ng-deep .mm-base-form-container .status-indicator{display:inline-flex;align-items:center;gap:.5rem;padding:.25rem .75rem;border-radius:var(--mm-form-radius);font-size:.875rem}::ng-deep .mm-base-form-container .status-indicator.success{background-color:var(--kendo-color-success-subtle);color:var(--kendo-color-success-on-subtle)}::ng-deep .mm-base-form-container .status-indicator.warning{background-color:var(--kendo-color-warning-subtle);color:var(--kendo-color-warning-on-subtle)}::ng-deep .mm-base-form-container .status-indicator.error{background-color:var(--kendo-color-error-subtle);color:var(--kendo-color-error-on-subtle)}::ng-deep .mm-base-form-container .status-indicator.info{background-color:var(--kendo-color-info-subtle);color:var(--kendo-color-info-on-subtle)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i4.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
4520
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.5", type: BaseFormComponent, isStandalone: true, selector: "mm-base-form", inputs: { form: "form", config: "config" }, outputs: { saveForm: "saveForm", cancelForm: "cancelForm" }, providers: [{ provide: HAS_UNSAVED_CHANGES, useExisting: BaseFormComponent }], queries: [{ propertyName: "titleExtra", first: true, predicate: FormTitleExtraDirective, descendants: true }], hostDirectives: [{ directive: UnsavedChangesDirective }], ngImport: i0, template: "<div class=\"mm-base-form-container\" [class.with-card]=\"config.showCard\" [class.without-card]=\"!config.showCard\">\n <!-- Title (no card mode) -->\n <div *ngIf=\"!config.showCard && (title || titleExtra)\" class=\"mm-form-title-row mm-form-title-standalone\">\n <h2 *ngIf=\"title\" class=\"mm-form-title\">{{ title }}</h2>\n <ng-container *ngIf=\"titleExtra\" [ngTemplateOutlet]=\"titleExtra.templateRef\"></ng-container>\n <span *ngIf=\"config.hasChanges\" class=\"mm-modified-badge\">{{ unsavedChangesText }}</span>\n </div>\n\n <!-- Main form wrapper - styled as card or plain based on CSS class -->\n <div class=\"mm-form-wrapper\" [style.maxWidth]=\"config.showCard ? (config.cardWidth || '800px') : null\">\n <!-- Card header (only in card mode) -->\n <div *ngIf=\"config.showCard && (title || titleExtra)\" class=\"mm-form-header\">\n <h2 *ngIf=\"title\" class=\"mm-form-title\">{{ title }}</h2>\n <ng-container *ngIf=\"titleExtra\" [ngTemplateOutlet]=\"titleExtra.templateRef\"></ng-container>\n <span *ngIf=\"config.hasChanges\" class=\"mm-modified-badge\">{{ unsavedChangesText }}</span>\n </div>\n\n <!-- Form body with content projection -->\n <div class=\"mm-form-body\">\n <div *ngIf=\"config.isLoading && config.showLoadingOverlay\" class=\"mm-loading-overlay\">\n <div class=\"k-loader k-loader-lg\"></div>\n <p>{{ loadingText }}</p>\n </div>\n <ng-content></ng-content>\n </div>\n\n <!-- Form actions -->\n <div class=\"mm-form-actions\">\n <!-- Status indicator (left side) -->\n <div class=\"mm-form-status\">\n <span class=\"mm-status-indicator\" [class.modified]=\"config.hasChanges\">\n <span class=\"mm-status-dot\"></span>\n <span class=\"mm-status-text\">{{ config.hasChanges ? modifiedText : readyText }}</span>\n </span>\n </div>\n\n <!-- Action buttons (right side) -->\n <div class=\"mm-form-buttons\">\n <button\n *ngIf=\"showSaveButton\"\n kendoButton\n type=\"submit\"\n [svgIcon]=\"saveIcon\"\n themeColor=\"primary\"\n [disabled]=\"saveButtonDisabled\"\n (click)=\"onSave()\">\n {{ saveButtonText }}\n </button>\n <button\n *ngIf=\"showCancelButton\"\n kendoButton\n type=\"button\"\n [svgIcon]=\"cancelIcon\"\n [disabled]=\"!!config.isSaving\"\n (click)=\"onCancel()\">\n {{ cancelButtonText }}\n </button>\n <ng-content select=\"[additionalActions]\"></ng-content>\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{--mm-form-bg: var(--kendo-color-surface);--mm-form-bg-alt: var(--kendo-color-surface-alt);--mm-form-bg-elevated: var(--kendo-color-surface-alt);--mm-form-border: var(--kendo-color-border);--mm-form-border-subtle: var(--kendo-color-border-alt, var(--mm-form-border));--mm-form-text: var(--kendo-color-on-app-surface);--mm-form-text-subtle: var(--kendo-color-subtle);--mm-form-text-label: var(--kendo-color-on-app-surface);--mm-form-accent: var(--kendo-color-primary);--mm-form-error: var(--kendo-color-error);--mm-form-status-ready: #37b400;--mm-form-status-modified: #da9162;--mm-form-padding: 1.5rem;--mm-form-padding-sm: 1rem;--mm-form-gap: .5rem;--mm-form-radius: 4px;--mm-form-shadow: 0 2px 6px rgba(0, 0, 0, .15);--mm-form-overlay-bg: var(--kendo-color-surface)}.mm-base-form-container{padding:var(--mm-form-padding);height:100%;overflow:auto}.mm-base-form-container .mm-form-wrapper{margin:0 auto}.mm-base-form-container.with-card .mm-form-wrapper{background:var(--mm-form-bg);border:1px solid var(--mm-form-border);border-radius:var(--mm-form-radius);box-shadow:var(--mm-form-shadow)}.mm-base-form-container.with-card .mm-form-header{display:flex;align-items:center;gap:1rem;padding:var(--mm-form-padding-sm) var(--mm-form-padding);border-bottom:1px solid var(--mm-form-border);background:var(--mm-form-bg-alt)}.mm-base-form-container.with-card .mm-form-body{padding:var(--mm-form-padding)}.mm-base-form-container.with-card .mm-form-actions{padding:var(--mm-form-padding-sm) var(--mm-form-padding);border-top:1px solid var(--mm-form-border);background:var(--mm-form-bg-alt)}.mm-base-form-container.without-card .mm-form-wrapper{max-width:1200px}.mm-base-form-container.without-card .mm-form-title-standalone{margin-bottom:var(--mm-form-padding)}.mm-base-form-container.without-card .mm-form-actions{margin-top:var(--mm-form-padding);padding-top:var(--mm-form-padding);border-top:1px solid var(--mm-form-border)}.mm-base-form-container .mm-form-title-row{display:flex;align-items:center;gap:1rem}.mm-base-form-container .mm-form-title{font-size:1.5rem;font-weight:500;margin:0;color:var(--mm-form-text)}.mm-base-form-container .mm-modified-badge{display:inline-flex;align-items:center;padding:.25rem .75rem;font-size:.75rem;font-weight:600;color:var(--mm-form-status-modified);background:#da916226;border:1px solid rgba(218,145,98,.4);border-radius:var(--mm-form-radius);animation:pulse-badge 2s ease-in-out infinite}@keyframes pulse-badge{0%,to{opacity:1}50%{opacity:.7}}.mm-base-form-container .mm-form-body{position:relative;min-height:100px}.mm-base-form-container .mm-form-actions{display:flex;gap:var(--mm-form-gap);justify-content:space-between;align-items:center}.mm-base-form-container .mm-form-status{display:flex;align-items:center}.mm-base-form-container .mm-status-indicator{display:flex;align-items:center;gap:.5rem;padding:.25rem .75rem;background:#37b4001a;border:1px solid rgba(55,180,0,.3);border-radius:var(--mm-form-radius)}.mm-base-form-container .mm-status-indicator .mm-status-dot{width:8px;height:8px;border-radius:50%;background:var(--mm-form-status-ready);box-shadow:0 0 8px #37b40099;animation:pulse-dot 2s ease-in-out infinite}.mm-base-form-container .mm-status-indicator .mm-status-text{font-size:.7rem;font-weight:600;color:var(--mm-form-status-ready)}.mm-base-form-container .mm-status-indicator.modified{background:#da91621a;border-color:#da91624d}.mm-base-form-container .mm-status-indicator.modified .mm-status-dot{background:var(--mm-form-status-modified);box-shadow:0 0 8px #da916299}.mm-base-form-container .mm-status-indicator.modified .mm-status-text{color:var(--mm-form-status-modified)}@keyframes pulse-dot{0%,to{opacity:1}50%{opacity:.6}}.mm-base-form-container .mm-form-buttons{display:flex;gap:var(--mm-form-gap)}.mm-base-form-container .mm-form-buttons button{min-width:100px}.mm-base-form-container .mm-loading-overlay{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;background-color:var(--mm-form-overlay-bg);opacity:.95;z-index:10}.mm-base-form-container .mm-loading-overlay p{margin-top:1rem;color:var(--mm-form-text-subtle);font-size:1rem}::ng-deep .mm-base-form-container fieldset[kendoformfieldset],::ng-deep .mm-base-form-container .k-form-fieldset{border:1px solid var(--mm-form-border);border-radius:var(--mm-form-radius);padding:var(--mm-form-padding);margin:0;background:var(--mm-form-bg-alt)}::ng-deep .mm-base-form-container fieldset[kendoformfieldset] legend,::ng-deep .mm-base-form-container fieldset[kendoformfieldset] .k-form-legend,::ng-deep .mm-base-form-container .k-form-fieldset legend,::ng-deep .mm-base-form-container .k-form-fieldset .k-form-legend{font-size:1.125rem;font-weight:500;color:var(--mm-form-accent);padding:0 .5rem}::ng-deep .mm-base-form-container kendo-formfield kendo-label,::ng-deep .mm-base-form-container kendo-formfield .k-label,::ng-deep .mm-base-form-container .k-form-field kendo-label,::ng-deep .mm-base-form-container .k-form-field .k-label{font-weight:500;color:var(--mm-form-text-label);margin-bottom:.25rem}::ng-deep .mm-base-form-container kendo-formfield kendo-label .k-required,::ng-deep .mm-base-form-container kendo-formfield .k-label .k-required,::ng-deep .mm-base-form-container .k-form-field kendo-label .k-required,::ng-deep .mm-base-form-container .k-form-field .k-label .k-required{color:var(--mm-form-error)}::ng-deep .mm-base-form-container kendo-formfield kendo-textbox,::ng-deep .mm-base-form-container kendo-formfield kendo-numerictextbox,::ng-deep .mm-base-form-container kendo-formfield kendo-textarea,::ng-deep .mm-base-form-container kendo-formfield kendo-datepicker,::ng-deep .mm-base-form-container kendo-formfield kendo-datetimepicker,::ng-deep .mm-base-form-container kendo-formfield kendo-timepicker,::ng-deep .mm-base-form-container kendo-formfield kendo-dropdownlist,::ng-deep .mm-base-form-container kendo-formfield kendo-combobox,::ng-deep .mm-base-form-container kendo-formfield kendo-multiselect,::ng-deep .mm-base-form-container kendo-formfield kendo-autocomplete,::ng-deep .mm-base-form-container .k-form-field kendo-textbox,::ng-deep .mm-base-form-container .k-form-field kendo-numerictextbox,::ng-deep .mm-base-form-container .k-form-field kendo-textarea,::ng-deep .mm-base-form-container .k-form-field kendo-datepicker,::ng-deep .mm-base-form-container .k-form-field kendo-datetimepicker,::ng-deep .mm-base-form-container .k-form-field kendo-timepicker,::ng-deep .mm-base-form-container .k-form-field kendo-dropdownlist,::ng-deep .mm-base-form-container .k-form-field kendo-combobox,::ng-deep .mm-base-form-container .k-form-field kendo-multiselect,::ng-deep .mm-base-form-container .k-form-field kendo-autocomplete{width:100%}::ng-deep .mm-base-form-container kendo-formfield kendo-formhint,::ng-deep .mm-base-form-container kendo-formfield .k-form-hint,::ng-deep .mm-base-form-container .k-form-field kendo-formhint,::ng-deep .mm-base-form-container .k-form-field .k-form-hint{color:var(--mm-form-text-subtle);font-size:.875rem}::ng-deep .mm-base-form-container kendo-formfield kendo-formerror,::ng-deep .mm-base-form-container kendo-formfield .k-form-error,::ng-deep .mm-base-form-container .k-form-field kendo-formerror,::ng-deep .mm-base-form-container .k-form-field .k-form-error{color:var(--mm-form-error);font-size:.875rem}::ng-deep .mm-base-form-container .status-indicator{display:inline-flex;align-items:center;gap:.5rem;padding:.25rem .75rem;border-radius:var(--mm-form-radius);font-size:.875rem}::ng-deep .mm-base-form-container .status-indicator.success{background-color:var(--kendo-color-success-subtle);color:var(--kendo-color-success-on-subtle)}::ng-deep .mm-base-form-container .status-indicator.warning{background-color:var(--kendo-color-warning-subtle);color:var(--kendo-color-warning-on-subtle)}::ng-deep .mm-base-form-container .status-indicator.error{background-color:var(--kendo-color-error-subtle);color:var(--kendo-color-error-on-subtle)}::ng-deep .mm-base-form-container .status-indicator.info{background-color:var(--kendo-color-info-subtle);color:var(--kendo-color-info-on-subtle)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
3348
4521
|
}
|
|
3349
4522
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: BaseFormComponent, decorators: [{
|
|
3350
4523
|
type: Component,
|
|
3351
4524
|
args: [{ selector: 'mm-base-form', standalone: true, imports: [
|
|
3352
4525
|
CommonModule,
|
|
3353
4526
|
ButtonModule
|
|
3354
|
-
], hostDirectives: [UnsavedChangesDirective], providers: [{ provide: HAS_UNSAVED_CHANGES, useExisting: BaseFormComponent }], changeDetection: ChangeDetectionStrategy.Eager, template: "<div class=\"mm-base-form-container\" [class.with-card]=\"config.showCard\" [class.without-card]=\"!config.showCard\">\n <!-- Title (no card mode) -->\n <div *ngIf=\"!config.showCard && (title || titleExtra)\" class=\"mm-form-title-row mm-form-title-standalone\">\n <h2 *ngIf=\"title\" class=\"mm-form-title\">{{ title }}</h2>\n <ng-container *ngIf=\"titleExtra\" [ngTemplateOutlet]=\"titleExtra.templateRef\"></ng-container>\n <span *ngIf=\"config.hasChanges\" class=\"mm-modified-badge\">{{ unsavedChangesText }}</span>\n </div>\n\n <!-- Main form wrapper - styled as card or plain based on CSS class -->\n <div class=\"mm-form-wrapper\" [style.maxWidth]=\"config.showCard ? (config.cardWidth || '800px') : null\">\n <!-- Card header (only in card mode) -->\n <div *ngIf=\"config.showCard && (title || titleExtra)\" class=\"mm-form-header\">\n <h2 *ngIf=\"title\" class=\"mm-form-title\">{{ title }}</h2>\n <ng-container *ngIf=\"titleExtra\" [ngTemplateOutlet]=\"titleExtra.templateRef\"></ng-container>\n <span *ngIf=\"config.hasChanges\" class=\"mm-modified-badge\">{{ unsavedChangesText }}</span>\n </div>\n\n <!-- Form body with content projection -->\n <div class=\"mm-form-body\">\n <div *ngIf=\"config.isLoading && config.showLoadingOverlay\" class=\"mm-loading-overlay\">\n <div class=\"k-loader k-loader-lg\"></div>\n <p>{{ loadingText }}</p>\n </div>\n <ng-content></ng-content>\n </div>\n\n <!-- Form actions -->\n <div class=\"mm-form-actions\">\n <!-- Status indicator (left side) -->\n <div class=\"mm-form-status\">\n <span class=\"mm-status-indicator\" [class.modified]=\"config.hasChanges\">\n <span class=\"mm-status-dot\"></span>\n <span class=\"mm-status-text\">{{ config.hasChanges ? modifiedText : readyText }}</span>\n </span>\n </div>\n\n <!-- Action buttons (right side) -->\n <div class=\"mm-form-buttons\">\n <button\n *ngIf=\"showSaveButton\"\n kendoButton\n type=\"submit\"\n [svgIcon]=\"saveIcon\"\n themeColor=\"primary\"\n [disabled]=\"saveButtonDisabled\"\n (click)=\"onSave()\">\n {{ saveButtonText }}\n </button>\n <button\n *ngIf=\"showCancelButton\"\n kendoButton\n type=\"button\"\n [svgIcon]=\"cancelIcon\"\n [disabled]=\"!!config.isSaving\"\n (click)=\"onCancel()\">\n {{ cancelButtonText }}\n </button>\n <ng-content select=\"[additionalActions]\"></ng-content>\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{--mm-form-bg: var(--kendo-color-surface);--mm-form-bg-alt: var(--kendo-color-surface-alt);--mm-form-bg-elevated: var(--kendo-color-surface-alt);--mm-form-border: var(--kendo-color-border);--mm-form-border-subtle: var(--kendo-color-border-alt, var(--mm-form-border));--mm-form-text: var(--kendo-color-on-app-surface);--mm-form-text-subtle: var(--kendo-color-subtle);--mm-form-text-label: var(--kendo-color-on-app-surface);--mm-form-accent: var(--kendo-color-primary);--mm-form-error: var(--kendo-color-error);--mm-form-status-ready: #37b400;--mm-form-status-modified: #da9162;--mm-form-padding: 1.5rem;--mm-form-padding-sm: 1rem;--mm-form-gap: .5rem;--mm-form-radius: 4px;--mm-form-shadow: 0 2px 6px rgba(0, 0, 0, .15);--mm-form-overlay-bg: var(--kendo-color-surface)}.mm-base-form-container{padding:var(--mm-form-padding);height:100%;overflow:auto}.mm-base-form-container .mm-form-wrapper{margin:0 auto}.mm-base-form-container.with-card .mm-form-wrapper{background:var(--mm-form-bg);border:1px solid var(--mm-form-border);border-radius:var(--mm-form-radius);box-shadow:var(--mm-form-shadow)}.mm-base-form-container.with-card .mm-form-header{display:flex;align-items:center;gap:1rem;padding:var(--mm-form-padding-sm) var(--mm-form-padding);border-bottom:1px solid var(--mm-form-border);background:var(--mm-form-bg-alt)}.mm-base-form-container.with-card .mm-form-body{padding:var(--mm-form-padding)}.mm-base-form-container.with-card .mm-form-actions{padding:var(--mm-form-padding-sm) var(--mm-form-padding);border-top:1px solid var(--mm-form-border);background:var(--mm-form-bg-alt)}.mm-base-form-container.without-card .mm-form-wrapper{max-width:1200px}.mm-base-form-container.without-card .mm-form-title-standalone{margin-bottom:var(--mm-form-padding)}.mm-base-form-container.without-card .mm-form-actions{margin-top:var(--mm-form-padding);padding-top:var(--mm-form-padding);border-top:1px solid var(--mm-form-border)}.mm-base-form-container .mm-form-title-row{display:flex;align-items:center;gap:1rem}.mm-base-form-container .mm-form-title{font-size:1.5rem;font-weight:500;margin:0;color:var(--mm-form-text)}.mm-base-form-container .mm-modified-badge{display:inline-flex;align-items:center;padding:.25rem .75rem;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--mm-form-status-modified);background:#da916226;border:1px solid rgba(218,145,98,.4);border-radius:var(--mm-form-radius);animation:pulse-badge 2s ease-in-out infinite}@keyframes pulse-badge{0%,to{opacity:1}50%{opacity:.7}}.mm-base-form-container .mm-form-body{position:relative;min-height:100px}.mm-base-form-container .mm-form-actions{display:flex;gap:var(--mm-form-gap);justify-content:space-between;align-items:center}.mm-base-form-container .mm-form-status{display:flex;align-items:center}.mm-base-form-container .mm-status-indicator{display:flex;align-items:center;gap:.5rem;padding:.25rem .75rem;background:#37b4001a;border:1px solid rgba(55,180,0,.3);border-radius:var(--mm-form-radius)}.mm-base-form-container .mm-status-indicator .mm-status-dot{width:8px;height:8px;border-radius:50%;background:var(--mm-form-status-ready);box-shadow:0 0 8px #37b40099;animation:pulse-dot 2s ease-in-out infinite}.mm-base-form-container .mm-status-indicator .mm-status-text{font-size:.7rem;font-weight:600;letter-spacing:1px;color:var(--mm-form-status-ready);text-transform:uppercase}.mm-base-form-container .mm-status-indicator.modified{background:#da91621a;border-color:#da91624d}.mm-base-form-container .mm-status-indicator.modified .mm-status-dot{background:var(--mm-form-status-modified);box-shadow:0 0 8px #da916299}.mm-base-form-container .mm-status-indicator.modified .mm-status-text{color:var(--mm-form-status-modified)}@keyframes pulse-dot{0%,to{opacity:1}50%{opacity:.6}}.mm-base-form-container .mm-form-buttons{display:flex;gap:var(--mm-form-gap)}.mm-base-form-container .mm-form-buttons button{min-width:100px}.mm-base-form-container .mm-loading-overlay{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;background-color:var(--mm-form-overlay-bg);opacity:.95;z-index:10}.mm-base-form-container .mm-loading-overlay p{margin-top:1rem;color:var(--mm-form-text-subtle);font-size:1rem}::ng-deep .mm-base-form-container fieldset[kendoformfieldset],::ng-deep .mm-base-form-container .k-form-fieldset{border:1px solid var(--mm-form-border);border-radius:var(--mm-form-radius);padding:var(--mm-form-padding);margin:0;background:var(--mm-form-bg-alt)}::ng-deep .mm-base-form-container fieldset[kendoformfieldset] legend,::ng-deep .mm-base-form-container fieldset[kendoformfieldset] .k-form-legend,::ng-deep .mm-base-form-container .k-form-fieldset legend,::ng-deep .mm-base-form-container .k-form-fieldset .k-form-legend{font-size:1.125rem;font-weight:500;color:var(--mm-form-accent);padding:0 .5rem}::ng-deep .mm-base-form-container kendo-formfield kendo-label,::ng-deep .mm-base-form-container kendo-formfield .k-label,::ng-deep .mm-base-form-container .k-form-field kendo-label,::ng-deep .mm-base-form-container .k-form-field .k-label{font-weight:500;color:var(--mm-form-text-label);margin-bottom:.25rem}::ng-deep .mm-base-form-container kendo-formfield kendo-label .k-required,::ng-deep .mm-base-form-container kendo-formfield .k-label .k-required,::ng-deep .mm-base-form-container .k-form-field kendo-label .k-required,::ng-deep .mm-base-form-container .k-form-field .k-label .k-required{color:var(--mm-form-error)}::ng-deep .mm-base-form-container kendo-formfield kendo-textbox,::ng-deep .mm-base-form-container kendo-formfield kendo-numerictextbox,::ng-deep .mm-base-form-container kendo-formfield kendo-textarea,::ng-deep .mm-base-form-container kendo-formfield kendo-datepicker,::ng-deep .mm-base-form-container kendo-formfield kendo-datetimepicker,::ng-deep .mm-base-form-container kendo-formfield kendo-timepicker,::ng-deep .mm-base-form-container kendo-formfield kendo-dropdownlist,::ng-deep .mm-base-form-container kendo-formfield kendo-combobox,::ng-deep .mm-base-form-container kendo-formfield kendo-multiselect,::ng-deep .mm-base-form-container kendo-formfield kendo-autocomplete,::ng-deep .mm-base-form-container .k-form-field kendo-textbox,::ng-deep .mm-base-form-container .k-form-field kendo-numerictextbox,::ng-deep .mm-base-form-container .k-form-field kendo-textarea,::ng-deep .mm-base-form-container .k-form-field kendo-datepicker,::ng-deep .mm-base-form-container .k-form-field kendo-datetimepicker,::ng-deep .mm-base-form-container .k-form-field kendo-timepicker,::ng-deep .mm-base-form-container .k-form-field kendo-dropdownlist,::ng-deep .mm-base-form-container .k-form-field kendo-combobox,::ng-deep .mm-base-form-container .k-form-field kendo-multiselect,::ng-deep .mm-base-form-container .k-form-field kendo-autocomplete{width:100%}::ng-deep .mm-base-form-container kendo-formfield kendo-formhint,::ng-deep .mm-base-form-container kendo-formfield .k-form-hint,::ng-deep .mm-base-form-container .k-form-field kendo-formhint,::ng-deep .mm-base-form-container .k-form-field .k-form-hint{color:var(--mm-form-text-subtle);font-size:.875rem}::ng-deep .mm-base-form-container kendo-formfield kendo-formerror,::ng-deep .mm-base-form-container kendo-formfield .k-form-error,::ng-deep .mm-base-form-container .k-form-field kendo-formerror,::ng-deep .mm-base-form-container .k-form-field .k-form-error{color:var(--mm-form-error);font-size:.875rem}::ng-deep .mm-base-form-container .status-indicator{display:inline-flex;align-items:center;gap:.5rem;padding:.25rem .75rem;border-radius:var(--mm-form-radius);font-size:.875rem}::ng-deep .mm-base-form-container .status-indicator.success{background-color:var(--kendo-color-success-subtle);color:var(--kendo-color-success-on-subtle)}::ng-deep .mm-base-form-container .status-indicator.warning{background-color:var(--kendo-color-warning-subtle);color:var(--kendo-color-warning-on-subtle)}::ng-deep .mm-base-form-container .status-indicator.error{background-color:var(--kendo-color-error-subtle);color:var(--kendo-color-error-on-subtle)}::ng-deep .mm-base-form-container .status-indicator.info{background-color:var(--kendo-color-info-subtle);color:var(--kendo-color-info-on-subtle)}\n"] }]
|
|
4527
|
+
], hostDirectives: [UnsavedChangesDirective], providers: [{ provide: HAS_UNSAVED_CHANGES, useExisting: BaseFormComponent }], changeDetection: ChangeDetectionStrategy.Eager, template: "<div class=\"mm-base-form-container\" [class.with-card]=\"config.showCard\" [class.without-card]=\"!config.showCard\">\n <!-- Title (no card mode) -->\n <div *ngIf=\"!config.showCard && (title || titleExtra)\" class=\"mm-form-title-row mm-form-title-standalone\">\n <h2 *ngIf=\"title\" class=\"mm-form-title\">{{ title }}</h2>\n <ng-container *ngIf=\"titleExtra\" [ngTemplateOutlet]=\"titleExtra.templateRef\"></ng-container>\n <span *ngIf=\"config.hasChanges\" class=\"mm-modified-badge\">{{ unsavedChangesText }}</span>\n </div>\n\n <!-- Main form wrapper - styled as card or plain based on CSS class -->\n <div class=\"mm-form-wrapper\" [style.maxWidth]=\"config.showCard ? (config.cardWidth || '800px') : null\">\n <!-- Card header (only in card mode) -->\n <div *ngIf=\"config.showCard && (title || titleExtra)\" class=\"mm-form-header\">\n <h2 *ngIf=\"title\" class=\"mm-form-title\">{{ title }}</h2>\n <ng-container *ngIf=\"titleExtra\" [ngTemplateOutlet]=\"titleExtra.templateRef\"></ng-container>\n <span *ngIf=\"config.hasChanges\" class=\"mm-modified-badge\">{{ unsavedChangesText }}</span>\n </div>\n\n <!-- Form body with content projection -->\n <div class=\"mm-form-body\">\n <div *ngIf=\"config.isLoading && config.showLoadingOverlay\" class=\"mm-loading-overlay\">\n <div class=\"k-loader k-loader-lg\"></div>\n <p>{{ loadingText }}</p>\n </div>\n <ng-content></ng-content>\n </div>\n\n <!-- Form actions -->\n <div class=\"mm-form-actions\">\n <!-- Status indicator (left side) -->\n <div class=\"mm-form-status\">\n <span class=\"mm-status-indicator\" [class.modified]=\"config.hasChanges\">\n <span class=\"mm-status-dot\"></span>\n <span class=\"mm-status-text\">{{ config.hasChanges ? modifiedText : readyText }}</span>\n </span>\n </div>\n\n <!-- Action buttons (right side) -->\n <div class=\"mm-form-buttons\">\n <button\n *ngIf=\"showSaveButton\"\n kendoButton\n type=\"submit\"\n [svgIcon]=\"saveIcon\"\n themeColor=\"primary\"\n [disabled]=\"saveButtonDisabled\"\n (click)=\"onSave()\">\n {{ saveButtonText }}\n </button>\n <button\n *ngIf=\"showCancelButton\"\n kendoButton\n type=\"button\"\n [svgIcon]=\"cancelIcon\"\n [disabled]=\"!!config.isSaving\"\n (click)=\"onCancel()\">\n {{ cancelButtonText }}\n </button>\n <ng-content select=\"[additionalActions]\"></ng-content>\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{--mm-form-bg: var(--kendo-color-surface);--mm-form-bg-alt: var(--kendo-color-surface-alt);--mm-form-bg-elevated: var(--kendo-color-surface-alt);--mm-form-border: var(--kendo-color-border);--mm-form-border-subtle: var(--kendo-color-border-alt, var(--mm-form-border));--mm-form-text: var(--kendo-color-on-app-surface);--mm-form-text-subtle: var(--kendo-color-subtle);--mm-form-text-label: var(--kendo-color-on-app-surface);--mm-form-accent: var(--kendo-color-primary);--mm-form-error: var(--kendo-color-error);--mm-form-status-ready: #37b400;--mm-form-status-modified: #da9162;--mm-form-padding: 1.5rem;--mm-form-padding-sm: 1rem;--mm-form-gap: .5rem;--mm-form-radius: 4px;--mm-form-shadow: 0 2px 6px rgba(0, 0, 0, .15);--mm-form-overlay-bg: var(--kendo-color-surface)}.mm-base-form-container{padding:var(--mm-form-padding);height:100%;overflow:auto}.mm-base-form-container .mm-form-wrapper{margin:0 auto}.mm-base-form-container.with-card .mm-form-wrapper{background:var(--mm-form-bg);border:1px solid var(--mm-form-border);border-radius:var(--mm-form-radius);box-shadow:var(--mm-form-shadow)}.mm-base-form-container.with-card .mm-form-header{display:flex;align-items:center;gap:1rem;padding:var(--mm-form-padding-sm) var(--mm-form-padding);border-bottom:1px solid var(--mm-form-border);background:var(--mm-form-bg-alt)}.mm-base-form-container.with-card .mm-form-body{padding:var(--mm-form-padding)}.mm-base-form-container.with-card .mm-form-actions{padding:var(--mm-form-padding-sm) var(--mm-form-padding);border-top:1px solid var(--mm-form-border);background:var(--mm-form-bg-alt)}.mm-base-form-container.without-card .mm-form-wrapper{max-width:1200px}.mm-base-form-container.without-card .mm-form-title-standalone{margin-bottom:var(--mm-form-padding)}.mm-base-form-container.without-card .mm-form-actions{margin-top:var(--mm-form-padding);padding-top:var(--mm-form-padding);border-top:1px solid var(--mm-form-border)}.mm-base-form-container .mm-form-title-row{display:flex;align-items:center;gap:1rem}.mm-base-form-container .mm-form-title{font-size:1.5rem;font-weight:500;margin:0;color:var(--mm-form-text)}.mm-base-form-container .mm-modified-badge{display:inline-flex;align-items:center;padding:.25rem .75rem;font-size:.75rem;font-weight:600;color:var(--mm-form-status-modified);background:#da916226;border:1px solid rgba(218,145,98,.4);border-radius:var(--mm-form-radius);animation:pulse-badge 2s ease-in-out infinite}@keyframes pulse-badge{0%,to{opacity:1}50%{opacity:.7}}.mm-base-form-container .mm-form-body{position:relative;min-height:100px}.mm-base-form-container .mm-form-actions{display:flex;gap:var(--mm-form-gap);justify-content:space-between;align-items:center}.mm-base-form-container .mm-form-status{display:flex;align-items:center}.mm-base-form-container .mm-status-indicator{display:flex;align-items:center;gap:.5rem;padding:.25rem .75rem;background:#37b4001a;border:1px solid rgba(55,180,0,.3);border-radius:var(--mm-form-radius)}.mm-base-form-container .mm-status-indicator .mm-status-dot{width:8px;height:8px;border-radius:50%;background:var(--mm-form-status-ready);box-shadow:0 0 8px #37b40099;animation:pulse-dot 2s ease-in-out infinite}.mm-base-form-container .mm-status-indicator .mm-status-text{font-size:.7rem;font-weight:600;color:var(--mm-form-status-ready)}.mm-base-form-container .mm-status-indicator.modified{background:#da91621a;border-color:#da91624d}.mm-base-form-container .mm-status-indicator.modified .mm-status-dot{background:var(--mm-form-status-modified);box-shadow:0 0 8px #da916299}.mm-base-form-container .mm-status-indicator.modified .mm-status-text{color:var(--mm-form-status-modified)}@keyframes pulse-dot{0%,to{opacity:1}50%{opacity:.6}}.mm-base-form-container .mm-form-buttons{display:flex;gap:var(--mm-form-gap)}.mm-base-form-container .mm-form-buttons button{min-width:100px}.mm-base-form-container .mm-loading-overlay{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;background-color:var(--mm-form-overlay-bg);opacity:.95;z-index:10}.mm-base-form-container .mm-loading-overlay p{margin-top:1rem;color:var(--mm-form-text-subtle);font-size:1rem}::ng-deep .mm-base-form-container fieldset[kendoformfieldset],::ng-deep .mm-base-form-container .k-form-fieldset{border:1px solid var(--mm-form-border);border-radius:var(--mm-form-radius);padding:var(--mm-form-padding);margin:0;background:var(--mm-form-bg-alt)}::ng-deep .mm-base-form-container fieldset[kendoformfieldset] legend,::ng-deep .mm-base-form-container fieldset[kendoformfieldset] .k-form-legend,::ng-deep .mm-base-form-container .k-form-fieldset legend,::ng-deep .mm-base-form-container .k-form-fieldset .k-form-legend{font-size:1.125rem;font-weight:500;color:var(--mm-form-accent);padding:0 .5rem}::ng-deep .mm-base-form-container kendo-formfield kendo-label,::ng-deep .mm-base-form-container kendo-formfield .k-label,::ng-deep .mm-base-form-container .k-form-field kendo-label,::ng-deep .mm-base-form-container .k-form-field .k-label{font-weight:500;color:var(--mm-form-text-label);margin-bottom:.25rem}::ng-deep .mm-base-form-container kendo-formfield kendo-label .k-required,::ng-deep .mm-base-form-container kendo-formfield .k-label .k-required,::ng-deep .mm-base-form-container .k-form-field kendo-label .k-required,::ng-deep .mm-base-form-container .k-form-field .k-label .k-required{color:var(--mm-form-error)}::ng-deep .mm-base-form-container kendo-formfield kendo-textbox,::ng-deep .mm-base-form-container kendo-formfield kendo-numerictextbox,::ng-deep .mm-base-form-container kendo-formfield kendo-textarea,::ng-deep .mm-base-form-container kendo-formfield kendo-datepicker,::ng-deep .mm-base-form-container kendo-formfield kendo-datetimepicker,::ng-deep .mm-base-form-container kendo-formfield kendo-timepicker,::ng-deep .mm-base-form-container kendo-formfield kendo-dropdownlist,::ng-deep .mm-base-form-container kendo-formfield kendo-combobox,::ng-deep .mm-base-form-container kendo-formfield kendo-multiselect,::ng-deep .mm-base-form-container kendo-formfield kendo-autocomplete,::ng-deep .mm-base-form-container .k-form-field kendo-textbox,::ng-deep .mm-base-form-container .k-form-field kendo-numerictextbox,::ng-deep .mm-base-form-container .k-form-field kendo-textarea,::ng-deep .mm-base-form-container .k-form-field kendo-datepicker,::ng-deep .mm-base-form-container .k-form-field kendo-datetimepicker,::ng-deep .mm-base-form-container .k-form-field kendo-timepicker,::ng-deep .mm-base-form-container .k-form-field kendo-dropdownlist,::ng-deep .mm-base-form-container .k-form-field kendo-combobox,::ng-deep .mm-base-form-container .k-form-field kendo-multiselect,::ng-deep .mm-base-form-container .k-form-field kendo-autocomplete{width:100%}::ng-deep .mm-base-form-container kendo-formfield kendo-formhint,::ng-deep .mm-base-form-container kendo-formfield .k-form-hint,::ng-deep .mm-base-form-container .k-form-field kendo-formhint,::ng-deep .mm-base-form-container .k-form-field .k-form-hint{color:var(--mm-form-text-subtle);font-size:.875rem}::ng-deep .mm-base-form-container kendo-formfield kendo-formerror,::ng-deep .mm-base-form-container kendo-formfield .k-form-error,::ng-deep .mm-base-form-container .k-form-field kendo-formerror,::ng-deep .mm-base-form-container .k-form-field .k-form-error{color:var(--mm-form-error);font-size:.875rem}::ng-deep .mm-base-form-container .status-indicator{display:inline-flex;align-items:center;gap:.5rem;padding:.25rem .75rem;border-radius:var(--mm-form-radius);font-size:.875rem}::ng-deep .mm-base-form-container .status-indicator.success{background-color:var(--kendo-color-success-subtle);color:var(--kendo-color-success-on-subtle)}::ng-deep .mm-base-form-container .status-indicator.warning{background-color:var(--kendo-color-warning-subtle);color:var(--kendo-color-warning-on-subtle)}::ng-deep .mm-base-form-container .status-indicator.error{background-color:var(--kendo-color-error-subtle);color:var(--kendo-color-error-on-subtle)}::ng-deep .mm-base-form-container .status-indicator.info{background-color:var(--kendo-color-info-subtle);color:var(--kendo-color-info-on-subtle)}\n"] }]
|
|
3355
4528
|
}], propDecorators: { titleExtra: [{
|
|
3356
4529
|
type: ContentChild,
|
|
3357
4530
|
args: [FormTitleExtraDirective]
|
|
@@ -3914,7 +5087,7 @@ class EntitySelectDialogComponent {
|
|
|
3914
5087
|
</button>
|
|
3915
5088
|
</div>
|
|
3916
5089
|
</div>
|
|
3917
|
-
`, isInline: true, styles: [":host{display:flex;flex-direction:column;height:100%}.entity-select-dialog-content{display:flex;flex-direction:column;flex:1;min-height:0;padding:16px 20px;box-sizing:border-box;gap:12px}.search-toolbar{flex-shrink:0}.entity-grid{flex:1;min-height:200px;border:1px solid var(--kendo-color-border)}.selection-info{padding:8px 0;font-size:12px;color:var(--kendo-color-subtle);flex-shrink:0}.dialog-actions{display:flex;justify-content:flex-end;gap:8px;flex-shrink:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$
|
|
5090
|
+
`, isInline: true, styles: [":host{display:flex;flex-direction:column;height:100%}.entity-select-dialog-content{display:flex;flex-direction:column;flex:1;min-height:0;padding:16px 20px;box-sizing:border-box;gap:12px}.search-toolbar{flex-shrink:0}.entity-grid{flex:1;min-height:200px;border:1px solid var(--kendo-color-border)}.selection-info{padding:8px 0;font-size:12px;color:var(--kendo-color-subtle);flex-shrink:0}.dialog-actions{display:flex;justify-content:flex-end;gap:8px;flex-shrink:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: WindowModule }, { kind: "component", type: GridComponent, selector: "kendo-grid", inputs: ["data", "pageSize", "height", "rowHeight", "adaptiveMode", "detailRowHeight", "skip", "scrollable", "selectable", "sort", "size", "trackBy", "filter", "group", "virtualColumns", "filterable", "sortable", "pageable", "groupable", "gridResizable", "rowReorderable", "navigable", "autoSize", "rowClass", "rowSticky", "rowSelected", "isRowSelectable", "cellSelected", "resizable", "reorderable", "loading", "columnMenu", "hideHeader", "showInactiveTools", "isDetailExpanded", "isGroupExpanded", "dataLayoutMode", "pinnable", "pinnedTopRows", "pinnedBottomRows", "isRowPinnable"], outputs: ["filterChange", "pageChange", "groupChange", "sortChange", "selectionChange", "rowReorder", "rowPinChange", "dataStateChange", "gridStateChange", "groupExpand", "groupCollapse", "detailExpand", "detailCollapse", "edit", "cancel", "save", "remove", "add", "cellClose", "cellClick", "pdfExport", "excelExport", "csvExport", "columnResize", "columnReorder", "columnVisibilityChange", "columnLockedChange", "columnStickyChange", "scrollBottom", "contentScroll"], exportAs: ["kendoGrid"] }, { kind: "component", type: ColumnComponent, selector: "kendo-grid-column", inputs: ["field", "format", "sortable", "groupable", "editor", "filter", "filterVariant", "filterable", "editable"] }, { kind: "component", type: CheckboxColumnComponent, selector: "kendo-grid-checkbox-column", inputs: ["showSelectAll", "showDisabledCheckbox"] }, { kind: "component", type: CustomMessagesComponent, selector: "kendo-grid-messages" }, { 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: "pipe", type: PascalCasePipe, name: "pascalCase" }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
3918
5091
|
}
|
|
3919
5092
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: EntitySelectDialogComponent, decorators: [{
|
|
3920
5093
|
type: Component,
|
|
@@ -4385,7 +5558,7 @@ class EntitySelectInputComponent {
|
|
|
4385
5558
|
(click)="openAdvancedSearch()">
|
|
4386
5559
|
</button>
|
|
4387
5560
|
</div>
|
|
4388
|
-
`, isInline: true, styles: [":host{display:block;width:100%}.entity-select-wrapper{position:relative;display:flex;align-items:center;width:100%;gap:4px}.entity-select-wrapper.disabled{opacity:.6;pointer-events:none}.entity-autocomplete{flex:1;min-width:0}.dialog-button{flex-shrink:0;height:30px;width:30px;padding:0;display:flex;align-items:center;justify-content:center}.clear-button{position:absolute;right:32px;top:50%;transform:translateY(-50%);z-index:1;padding:4px;min-width:auto;height:20px;width:20px}.entity-item{padding:4px 0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.no-data-message{padding:8px 12px;color:var(--kendo-color-subtle);font-style:italic;text-align:center}.advanced-search-footer{display:flex;align-items:center;gap:8px;padding:8px 12px;cursor:pointer;color:var(--kendo-color-primary);border-top:1px solid var(--kendo-color-border);background:var(--kendo-color-surface-alt);transition:background-color .2s}.advanced-search-footer:hover{background:var(--kendo-color-base-hover)}::ng-deep .entity-autocomplete .k-input-inner{padding-right:60px!important}::ng-deep .entity-autocomplete.k-disabled .k-input-inner{padding-right:32px!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$
|
|
5561
|
+
`, isInline: true, styles: [":host{display:block;width:100%}.entity-select-wrapper{position:relative;display:flex;align-items:center;width:100%;gap:4px}.entity-select-wrapper.disabled{opacity:.6;pointer-events:none}.entity-autocomplete{flex:1;min-width:0}.dialog-button{flex-shrink:0;height:30px;width:30px;padding:0;display:flex;align-items:center;justify-content:center}.clear-button{position:absolute;right:32px;top:50%;transform:translateY(-50%);z-index:1;padding:4px;min-width:auto;height:20px;width:20px}.entity-item{padding:4px 0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.no-data-message{padding:8px 12px;color:var(--kendo-color-subtle);font-style:italic;text-align:center}.advanced-search-footer{display:flex;align-items:center;gap:8px;padding:8px 12px;cursor:pointer;color:var(--kendo-color-primary);border-top:1px solid var(--kendo-color-border);background:var(--kendo-color-surface-alt);transition:background-color .2s}.advanced-search-footer:hover{background:var(--kendo-color-base-hover)}::ng-deep .entity-autocomplete .k-input-inner{padding-right:60px!important}::ng-deep .entity-autocomplete.k-disabled .k-input-inner{padding-right:32px!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i3.AutoCompleteComponent, selector: "kendo-autocomplete", inputs: ["highlightFirst", "showStickyHeader", "focusableId", "data", "value", "valueField", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "loading", "clearButton", "suggest", "disabled", "itemDisabled", "readonly", "tabindex", "tabIndex", "filterable", "virtual", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "filterChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoAutoComplete"] }, { kind: "directive", type: i3.FooterTemplateDirective, selector: "[kendoDropDownListFooterTemplate],[kendoComboBoxFooterTemplate],[kendoDropDownTreeFooterTemplate],[kendoMultiColumnComboBoxFooterTemplate],[kendoAutoCompleteFooterTemplate],[kendoMultiSelectFooterTemplate],[kendoMultiSelectTreeFooterTemplate]" }, { kind: "directive", type: i3.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "directive", type: i3.NoDataTemplateDirective, selector: "[kendoDropDownListNoDataTemplate],[kendoDropDownTreeNoDataTemplate],[kendoComboBoxNoDataTemplate],[kendoMultiColumnComboBoxNoDataTemplate],[kendoAutoCompleteNoDataTemplate],[kendoMultiSelectNoDataTemplate],[kendoMultiSelectTreeNoDataTemplate]" }, { kind: "ngmodule", type: LoaderModule }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: IconsModule }, { kind: "component", type: i2.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon", "variant"], exportAs: ["kendoSVGIcon"] }, { kind: "ngmodule", type: SVGIconModule }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
4389
5562
|
}
|
|
4390
5563
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: EntitySelectInputComponent, decorators: [{
|
|
4391
5564
|
type: Component,
|
|
@@ -4782,7 +5955,7 @@ class SaveAsDialogComponent extends DialogContentBase {
|
|
|
4782
5955
|
{{ saveButtonText }}
|
|
4783
5956
|
</button>
|
|
4784
5957
|
</kendo-dialog-actions>
|
|
4785
|
-
`, isInline: true, styles: [".save-as-container{padding:20px;min-width:350px}.form-group{display:flex;flex-direction:column;gap:8px}.name-input{width:100%}.validation-container{min-height:24px}.validation-message{font-size:12px;padding:4px 0}.validation-message.error{color:var(--kendo-color-error, #dc3545)}.validation-message.success{color:var(--kendo-color-success, #28a745)}.availability-check{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--kendo-color-subtle, #6c757d)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$
|
|
5958
|
+
`, isInline: true, styles: [".save-as-container{padding:20px;min-width:350px}.form-group{display:flex;flex-direction:column;gap:8px}.name-input{width:100%}.validation-container{min-height:24px}.validation-message{font-size:12px;padding:4px 0}.validation-message.error{color:var(--kendo-color-error, #dc3545)}.validation-message.success{color:var(--kendo-color-success, #28a745)}.availability-check{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--kendo-color-subtle, #6c757d)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i3$1.DialogActionsComponent, selector: "kendo-dialog-actions", inputs: ["actions", "layout"], outputs: ["action"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i5.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: "ngmodule", type: LabelModule }, { kind: "component", type: i6.LabelComponent, selector: "kendo-label", inputs: ["text", "for", "optional", "labelCssStyle", "labelCssClass"], exportAs: ["kendoLabel"] }, { kind: "ngmodule", type: IndicatorsModule }, { kind: "component", type: i7.LoaderComponent, selector: "kendo-loader", inputs: ["type", "themeColor", "size"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
4786
5959
|
}
|
|
4787
5960
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: SaveAsDialogComponent, decorators: [{
|
|
4788
5961
|
type: Component,
|
|
@@ -5501,7 +6674,7 @@ class TimeRangePickerComponent {
|
|
|
5501
6674
|
return this.selectedType() === type;
|
|
5502
6675
|
}
|
|
5503
6676
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TimeRangePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5504
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: TimeRangePickerComponent, isStandalone: true, selector: "mm-time-range-picker", inputs: { config: "config", labels: "labels", initialSelection: "initialSelection" }, outputs: { rangeChange: "rangeChange", rangeChangeISO: "rangeChangeISO", selectionChange: "selectionChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"time-range-picker\">\n <!-- Range Type Selection -->\n <div class=\"picker-field type-field\">\n <kendo-label [text]=\"mergedLabels().rangeType || 'Range Type'\">\n <kendo-dropdownlist\n [data]=\"typeOptions()\"\n [value]=\"selectedType()\"\n textField=\"label\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n (valueChange)=\"onTypeChange($event)\">\n </kendo-dropdownlist>\n </kendo-label>\n </div>\n\n <!-- Year Selection (for year, quarter, month, day types) -->\n @if (isType('year') || isType('quarter') || isType('month') || isType('day')) {\n <div class=\"picker-field year-field\">\n <kendo-label [text]=\"mergedLabels().year || 'Year'\">\n <kendo-dropdownlist\n [data]=\"yearOptions()\"\n [value]=\"selectedYear()\"\n textField=\"label\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n (valueChange)=\"onYearChange($event)\">\n </kendo-dropdownlist>\n </kendo-label>\n </div>\n }\n\n <!-- Quarter Selection -->\n @if (isType('quarter')) {\n <div class=\"picker-field quarter-field\">\n <kendo-label [text]=\"mergedLabels().quarter || 'Quarter'\">\n <kendo-dropdownlist\n [data]=\"quarterOptions()\"\n [value]=\"selectedQuarter()\"\n textField=\"label\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n (valueChange)=\"onQuarterChange($event)\">\n </kendo-dropdownlist>\n </kendo-label>\n </div>\n }\n\n <!-- Month Selection -->\n @if (isType('month') || isType('day')) {\n <div class=\"picker-field month-field\">\n <kendo-label [text]=\"mergedLabels().month || 'Month'\">\n <kendo-dropdownlist\n [data]=\"monthOptions()\"\n [value]=\"selectedMonth()\"\n textField=\"label\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n (valueChange)=\"onMonthChange($event)\">\n </kendo-dropdownlist>\n </kendo-label>\n </div>\n }\n\n <!-- Day Selection -->\n @if (isType('day')) {\n <div class=\"picker-field day-field\">\n <kendo-label [text]=\"mergedLabels().day || 'Day'\">\n <kendo-dropdownlist\n [data]=\"dayOptions()\"\n [value]=\"selectedDay()\"\n textField=\"label\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n (valueChange)=\"onDayChange($event)\">\n </kendo-dropdownlist>\n </kendo-label>\n </div>\n\n <!-- Hour From Selection (optional) -->\n <div class=\"picker-field hour-from-field\">\n <kendo-label [text]=\"mergedLabels().hourFrom || 'From Hour'\">\n <kendo-dropdownlist\n [data]=\"hourFromOptions()\"\n [value]=\"hourFrom()\"\n textField=\"label\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n [defaultItem]=\"{ value: null, label: '\u2014' }\"\n (valueChange)=\"onHourFromChange($event)\">\n </kendo-dropdownlist>\n </kendo-label>\n </div>\n\n <!-- Hour To Selection (visible when hourFrom is set) -->\n @if (hourFrom() !== null) {\n <div class=\"picker-field hour-to-field\">\n <kendo-label [text]=\"mergedLabels().hourTo || 'To Hour'\">\n <kendo-dropdownlist\n [data]=\"hourToOptions()\"\n [value]=\"hourTo()\"\n textField=\"label\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n (valueChange)=\"onHourToChange($event)\">\n </kendo-dropdownlist>\n </kendo-label>\n </div>\n }\n }\n\n <!-- Relative Time Selection -->\n @if (isType('relative')) {\n <div class=\"picker-field relative-value-field\">\n <kendo-label [text]=\"mergedLabels().relativeValue || 'Last'\">\n <kendo-numerictextbox\n [value]=\"relativeValue()\"\n [min]=\"1\"\n [max]=\"9999\"\n [decimals]=\"0\"\n [format]=\"'n0'\"\n [spinners]=\"true\"\n (valueChange)=\"onRelativeValueChange($event)\">\n </kendo-numerictextbox>\n </kendo-label>\n </div>\n\n <div class=\"picker-field relative-unit-field\">\n <kendo-label [text]=\"mergedLabels().relativeUnit || 'Unit'\">\n <kendo-dropdownlist\n [data]=\"relativeUnitOptions()\"\n [value]=\"relativeUnit()\"\n textField=\"label\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n (valueChange)=\"onRelativeUnitChange($event)\">\n </kendo-dropdownlist>\n </kendo-label>\n </div>\n }\n\n <!-- Custom Date Range -->\n @if (isType('custom')) {\n <div class=\"picker-field custom-from-field\">\n <kendo-label [text]=\"mergedLabels().customFrom || 'From'\">\n @if (showTime()) {\n <kendo-datetimepicker\n [value]=\"customFrom()\"\n [min]=\"minDate()\"\n [max]=\"customTo()\"\n (valueChange)=\"onCustomFromChange($event)\">\n </kendo-datetimepicker>\n } @else {\n <kendo-datepicker\n [value]=\"customFrom()\"\n [min]=\"minDate()\"\n [max]=\"customTo()\"\n (valueChange)=\"onCustomFromChange($event)\">\n </kendo-datepicker>\n }\n </kendo-label>\n </div>\n\n <div class=\"picker-field custom-to-field\">\n <kendo-label [text]=\"mergedLabels().customTo || 'To'\">\n @if (showTime()) {\n <kendo-datetimepicker\n [value]=\"customTo()\"\n [min]=\"customFrom()\"\n [max]=\"maxDate()\"\n (valueChange)=\"onCustomToChange($event)\">\n </kendo-datetimepicker>\n } @else {\n <kendo-datepicker\n [value]=\"customTo()\"\n [min]=\"customFrom()\"\n [max]=\"maxDate()\"\n (valueChange)=\"onCustomToChange($event)\">\n </kendo-datepicker>\n }\n </kendo-label>\n </div>\n }\n</div>\n", styles: [".time-range-picker{display:flex;flex-wrap:wrap;gap:1rem;align-items:flex-end}.time-range-picker .picker-field{display:flex;flex-direction:column}.time-range-picker .picker-field kendo-label{display:flex;flex-direction:column;gap:.25rem}.time-range-picker .type-field{min-width:120px}.time-range-picker .year-field{min-width:100px}.time-range-picker .quarter-field{min-width:140px}.time-range-picker .month-field{min-width:130px}.time-range-picker .relative-value-field{min-width:80px}.time-range-picker .relative-value-field kendo-numerictextbox{width:100%}.time-range-picker .relative-unit-field{min-width:100px}.time-range-picker .custom-from-field,.time-range-picker .custom-to-field{min-width:150px}.time-range-picker .custom-from-field kendo-datepicker,.time-range-picker .custom-from-field kendo-datetimepicker,.time-range-picker .custom-to-field kendo-datepicker,.time-range-picker .custom-to-field kendo-datetimepicker{width:100%}@media(max-width:768px){.time-range-picker{gap:.75rem}.time-range-picker .picker-field{flex:1 1 calc(50% - .5rem);min-width:0}.time-range-picker .type-field{flex:1 1 100%}}@media(max-width:480px){.time-range-picker{flex-direction:column;gap:.5rem}.time-range-picker .picker-field{flex:1 1 100%;width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: DropDownListModule }, { kind: "component", type: i3$1.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "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: "ngmodule", type: DatePickerModule }, { kind: "component", type: i2$1.DatePickerComponent, selector: "kendo-datepicker", inputs: ["focusableId", "cellTemplate", "clearButton", "inputAttributes", "monthCellTemplate", "yearCellTemplate", "decadeCellTemplate", "centuryCellTemplate", "weekNumberTemplate", "headerTitleTemplate", "headerTemplate", "footerTemplate", "footer", "navigationItemTemplate", "weekDaysFormat", "showOtherMonthDays", "activeView", "bottomView", "topView", "calendarType", "animateCalendarNavigation", "disabled", "readonly", "readOnlyInput", "popupSettings", "navigation", "min", "max", "incompleteDateValidation", "autoCorrectParts", "autoSwitchParts", "autoSwitchKeys", "enableMouseWheel", "allowCaretMode", "autoFill", "focusedDate", "value", "format", "twoDigitYearMax", "formatPlaceholder", "placeholder", "tabindex", "tabIndex", "disabledDates", "adaptiveTitle", "adaptiveSubtitle", "rangeValidation", "disabledDatesValidation", "weekNumber", "size", "rounded", "fillMode", "adaptiveMode"], outputs: ["valueChange", "focus", "blur", "open", "close", "escape"], exportAs: ["kendo-datepicker"] }, { kind: "ngmodule", type: DateTimePickerModule }, { kind: "component", type: i2$1.DateTimePickerComponent, selector: "kendo-datetimepicker", inputs: ["focusableId", "weekDaysFormat", "showOtherMonthDays", "value", "format", "twoDigitYearMax", "tabindex", "disabledDates", "popupSettings", "adaptiveTitle", "adaptiveSubtitle", "disabled", "readonly", "readOnlyInput", "cancelButton", "formatPlaceholder", "placeholder", "steps", "focusedDate", "calendarType", "animateCalendarNavigation", "weekNumber", "min", "max", "rangeValidation", "disabledDatesValidation", "incompleteDateValidation", "autoCorrectParts", "autoSwitchParts", "autoSwitchKeys", "enableMouseWheel", "allowCaretMode", "clearButton", "autoFill", "adaptiveMode", "inputAttributes", "defaultTab", "size", "rounded", "fillMode", "headerTemplate", "footerTemplate", "footer"], outputs: ["valueChange", "open", "close", "focus", "blur", "escape"], exportAs: ["kendo-datetimepicker"] }, { kind: "ngmodule", type: NumericTextBoxModule }, { kind: "component", type: i5.NumericTextBoxComponent, selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "ngmodule", type: LabelModule }, { kind: "component", type: i6.LabelComponent, selector: "kendo-label", inputs: ["text", "for", "optional", "labelCssStyle", "labelCssClass"], exportAs: ["kendoLabel"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
6677
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: TimeRangePickerComponent, isStandalone: true, selector: "mm-time-range-picker", inputs: { config: "config", labels: "labels", initialSelection: "initialSelection" }, outputs: { rangeChange: "rangeChange", rangeChangeISO: "rangeChangeISO", selectionChange: "selectionChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"time-range-picker\">\n <!-- Range Type Selection -->\n <div class=\"picker-field type-field\">\n <kendo-label [text]=\"mergedLabels().rangeType || 'Range Type'\">\n <kendo-dropdownlist\n [data]=\"typeOptions()\"\n [value]=\"selectedType()\"\n textField=\"label\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n (valueChange)=\"onTypeChange($event)\">\n </kendo-dropdownlist>\n </kendo-label>\n </div>\n\n <!-- Year Selection (for year, quarter, month, day types) -->\n @if (isType('year') || isType('quarter') || isType('month') || isType('day')) {\n <div class=\"picker-field year-field\">\n <kendo-label [text]=\"mergedLabels().year || 'Year'\">\n <kendo-dropdownlist\n [data]=\"yearOptions()\"\n [value]=\"selectedYear()\"\n textField=\"label\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n (valueChange)=\"onYearChange($event)\">\n </kendo-dropdownlist>\n </kendo-label>\n </div>\n }\n\n <!-- Quarter Selection -->\n @if (isType('quarter')) {\n <div class=\"picker-field quarter-field\">\n <kendo-label [text]=\"mergedLabels().quarter || 'Quarter'\">\n <kendo-dropdownlist\n [data]=\"quarterOptions()\"\n [value]=\"selectedQuarter()\"\n textField=\"label\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n (valueChange)=\"onQuarterChange($event)\">\n </kendo-dropdownlist>\n </kendo-label>\n </div>\n }\n\n <!-- Month Selection -->\n @if (isType('month') || isType('day')) {\n <div class=\"picker-field month-field\">\n <kendo-label [text]=\"mergedLabels().month || 'Month'\">\n <kendo-dropdownlist\n [data]=\"monthOptions()\"\n [value]=\"selectedMonth()\"\n textField=\"label\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n (valueChange)=\"onMonthChange($event)\">\n </kendo-dropdownlist>\n </kendo-label>\n </div>\n }\n\n <!-- Day Selection -->\n @if (isType('day')) {\n <div class=\"picker-field day-field\">\n <kendo-label [text]=\"mergedLabels().day || 'Day'\">\n <kendo-dropdownlist\n [data]=\"dayOptions()\"\n [value]=\"selectedDay()\"\n textField=\"label\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n (valueChange)=\"onDayChange($event)\">\n </kendo-dropdownlist>\n </kendo-label>\n </div>\n\n <!-- Hour From Selection (optional) -->\n <div class=\"picker-field hour-from-field\">\n <kendo-label [text]=\"mergedLabels().hourFrom || 'From Hour'\">\n <kendo-dropdownlist\n [data]=\"hourFromOptions()\"\n [value]=\"hourFrom()\"\n textField=\"label\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n [defaultItem]=\"{ value: null, label: '\u2014' }\"\n (valueChange)=\"onHourFromChange($event)\">\n </kendo-dropdownlist>\n </kendo-label>\n </div>\n\n <!-- Hour To Selection (visible when hourFrom is set) -->\n @if (hourFrom() !== null) {\n <div class=\"picker-field hour-to-field\">\n <kendo-label [text]=\"mergedLabels().hourTo || 'To Hour'\">\n <kendo-dropdownlist\n [data]=\"hourToOptions()\"\n [value]=\"hourTo()\"\n textField=\"label\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n (valueChange)=\"onHourToChange($event)\">\n </kendo-dropdownlist>\n </kendo-label>\n </div>\n }\n }\n\n <!-- Relative Time Selection -->\n @if (isType('relative')) {\n <div class=\"picker-field relative-value-field\">\n <kendo-label [text]=\"mergedLabels().relativeValue || 'Last'\">\n <kendo-numerictextbox\n [value]=\"relativeValue()\"\n [min]=\"1\"\n [max]=\"9999\"\n [decimals]=\"0\"\n [format]=\"'n0'\"\n [spinners]=\"true\"\n (valueChange)=\"onRelativeValueChange($event)\">\n </kendo-numerictextbox>\n </kendo-label>\n </div>\n\n <div class=\"picker-field relative-unit-field\">\n <kendo-label [text]=\"mergedLabels().relativeUnit || 'Unit'\">\n <kendo-dropdownlist\n [data]=\"relativeUnitOptions()\"\n [value]=\"relativeUnit()\"\n textField=\"label\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n (valueChange)=\"onRelativeUnitChange($event)\">\n </kendo-dropdownlist>\n </kendo-label>\n </div>\n }\n\n <!-- Custom Date Range -->\n @if (isType('custom')) {\n <div class=\"picker-field custom-from-field\">\n <kendo-label [text]=\"mergedLabels().customFrom || 'From'\">\n @if (showTime()) {\n <kendo-datetimepicker\n [value]=\"customFrom()\"\n [min]=\"minDate()\"\n [max]=\"customTo()\"\n (valueChange)=\"onCustomFromChange($event)\">\n </kendo-datetimepicker>\n } @else {\n <kendo-datepicker\n [value]=\"customFrom()\"\n [min]=\"minDate()\"\n [max]=\"customTo()\"\n (valueChange)=\"onCustomFromChange($event)\">\n </kendo-datepicker>\n }\n </kendo-label>\n </div>\n\n <div class=\"picker-field custom-to-field\">\n <kendo-label [text]=\"mergedLabels().customTo || 'To'\">\n @if (showTime()) {\n <kendo-datetimepicker\n [value]=\"customTo()\"\n [min]=\"customFrom()\"\n [max]=\"maxDate()\"\n (valueChange)=\"onCustomToChange($event)\">\n </kendo-datetimepicker>\n } @else {\n <kendo-datepicker\n [value]=\"customTo()\"\n [min]=\"customFrom()\"\n [max]=\"maxDate()\"\n (valueChange)=\"onCustomToChange($event)\">\n </kendo-datepicker>\n }\n </kendo-label>\n </div>\n }\n</div>\n", styles: [".time-range-picker{display:flex;flex-wrap:wrap;gap:1rem;align-items:flex-end}.time-range-picker .picker-field{display:flex;flex-direction:column}.time-range-picker .picker-field kendo-label{display:flex;flex-direction:column;gap:.25rem}.time-range-picker .type-field{min-width:120px}.time-range-picker .year-field{min-width:100px}.time-range-picker .quarter-field{min-width:140px}.time-range-picker .month-field{min-width:130px}.time-range-picker .relative-value-field{min-width:80px}.time-range-picker .relative-value-field kendo-numerictextbox{width:100%}.time-range-picker .relative-unit-field{min-width:100px}.time-range-picker .custom-from-field,.time-range-picker .custom-to-field{min-width:150px}.time-range-picker .custom-from-field kendo-datepicker,.time-range-picker .custom-from-field kendo-datetimepicker,.time-range-picker .custom-to-field kendo-datepicker,.time-range-picker .custom-to-field kendo-datetimepicker{width:100%}@media(max-width:768px){.time-range-picker{gap:.75rem}.time-range-picker .picker-field{flex:1 1 calc(50% - .5rem);min-width:0}.time-range-picker .type-field{flex:1 1 100%}}@media(max-width:480px){.time-range-picker{flex-direction:column;gap:.5rem}.time-range-picker .picker-field{flex:1 1 100%;width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: DropDownListModule }, { kind: "component", type: i3.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "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: "ngmodule", type: DatePickerModule }, { kind: "component", type: i2$2.DatePickerComponent, selector: "kendo-datepicker", inputs: ["focusableId", "cellTemplate", "clearButton", "inputAttributes", "monthCellTemplate", "yearCellTemplate", "decadeCellTemplate", "centuryCellTemplate", "weekNumberTemplate", "headerTitleTemplate", "headerTemplate", "footerTemplate", "footer", "navigationItemTemplate", "weekDaysFormat", "showOtherMonthDays", "activeView", "bottomView", "topView", "calendarType", "animateCalendarNavigation", "disabled", "readonly", "readOnlyInput", "popupSettings", "navigation", "min", "max", "incompleteDateValidation", "autoCorrectParts", "autoSwitchParts", "autoSwitchKeys", "enableMouseWheel", "allowCaretMode", "autoFill", "focusedDate", "value", "format", "twoDigitYearMax", "formatPlaceholder", "placeholder", "tabindex", "tabIndex", "disabledDates", "adaptiveTitle", "adaptiveSubtitle", "rangeValidation", "disabledDatesValidation", "weekNumber", "size", "rounded", "fillMode", "adaptiveMode"], outputs: ["valueChange", "focus", "blur", "open", "close", "escape"], exportAs: ["kendo-datepicker"] }, { kind: "ngmodule", type: DateTimePickerModule }, { kind: "component", type: i2$2.DateTimePickerComponent, selector: "kendo-datetimepicker", inputs: ["focusableId", "weekDaysFormat", "showOtherMonthDays", "value", "format", "twoDigitYearMax", "tabindex", "disabledDates", "popupSettings", "adaptiveTitle", "adaptiveSubtitle", "disabled", "readonly", "readOnlyInput", "cancelButton", "formatPlaceholder", "placeholder", "steps", "focusedDate", "calendarType", "animateCalendarNavigation", "weekNumber", "min", "max", "rangeValidation", "disabledDatesValidation", "incompleteDateValidation", "autoCorrectParts", "autoSwitchParts", "autoSwitchKeys", "enableMouseWheel", "allowCaretMode", "clearButton", "autoFill", "adaptiveMode", "inputAttributes", "defaultTab", "size", "rounded", "fillMode", "headerTemplate", "footerTemplate", "footer"], outputs: ["valueChange", "open", "close", "focus", "blur", "escape"], exportAs: ["kendo-datetimepicker"] }, { kind: "ngmodule", type: NumericTextBoxModule }, { kind: "component", type: i5.NumericTextBoxComponent, selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "ngmodule", type: LabelModule }, { kind: "component", type: i6.LabelComponent, selector: "kendo-label", inputs: ["text", "for", "optional", "labelCssStyle", "labelCssClass"], exportAs: ["kendoLabel"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
5505
6678
|
}
|
|
5506
6679
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TimeRangePickerComponent, decorators: [{
|
|
5507
6680
|
type: Component,
|
|
@@ -6397,7 +7570,7 @@ class CronBuilderComponent {
|
|
|
6397
7570
|
useExisting: forwardRef(() => CronBuilderComponent),
|
|
6398
7571
|
multi: true
|
|
6399
7572
|
}
|
|
6400
|
-
], ngImport: i0, template: "<div class=\"cron-builder\" [class.disabled]=\"disabled()\">\n <!-- Expression Preview Section -->\n <div class=\"expression-section\">\n <div class=\"expression-display\">\n <div class=\"expression-label\">Cron Expression</div>\n <div class=\"expression-value-row\">\n <code class=\"expression-value\" [class.invalid]=\"!validationResult().isValid\">\n {{ expression() }}\n </code>\n @if (mergedConfig().showCopyButton) {\n <button\n kendoButton\n [svgIcon]=\"copiedRecently() ? checkIcon : copyIcon\"\n [title]=\"copiedRecently() ? 'Copied!' : 'Copy to clipboard'\"\n fillMode=\"flat\"\n class=\"copy-button\"\n [class.copied]=\"copiedRecently()\"\n (click)=\"onCopyExpression()\"\n [disabled]=\"disabled()\">\n </button>\n }\n </div>\n @if (!validationResult().isValid) {\n <div class=\"validation-error\">\n <kendo-svg-icon [icon]=\"xIcon\" size=\"small\"></kendo-svg-icon>\n {{ validationResult().error }}\n </div>\n }\n </div>\n\n @if (mergedConfig().showHumanReadable && validationResult().isValid) {\n <div class=\"human-readable\">\n <kendo-svg-icon [icon]=\"clockIcon\" size=\"small\"></kendo-svg-icon>\n <span>{{ humanReadable() }}</span>\n </div>\n }\n </div>\n\n <!-- Preset Buttons -->\n @if (mergedConfig().showPresets) {\n <div class=\"presets-section\">\n <div class=\"presets-label\">Quick Select</div>\n <div class=\"presets-grid\">\n @for (preset of presetsByCategory().frequent; track preset.id) {\n <button\n kendoButton\n size=\"small\"\n fillMode=\"outline\"\n class=\"preset-button\"\n [title]=\"preset.description\"\n [disabled]=\"disabled()\"\n (click)=\"onPresetSelect(preset)\">\n {{ preset.label }}\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Schedule Type Tabs -->\n <kendo-tabstrip\n [animate]=\"false\"\n [tabAlignment]=\"'start'\"\n (tabSelect)=\"onTabSelect($event.index)\">\n\n <!-- Seconds Tab -->\n <kendo-tabstrip-tab [title]=\"'Seconds'\" [selected]=\"selectedTabIndex() === 0\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row\">\n <kendo-label text=\"Run every\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"secondIntervalOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"secondInterval()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onSecondIntervalChange($event)\">\n </kendo-dropdownlist>\n </div>\n <div class=\"preview-text\">\n Generates: <code>*/{{ secondInterval() }} * * * * *</code>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Minutes Tab -->\n <kendo-tabstrip-tab [title]=\"'Minutes'\" [selected]=\"selectedTabIndex() === 1\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row\">\n <kendo-label text=\"Run every\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"minuteIntervalOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"minuteInterval()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onMinuteIntervalChange($event)\">\n </kendo-dropdownlist>\n </div>\n <div class=\"preview-text\">\n Generates: <code>0 */{{ minuteInterval() }} * * * *</code>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Hourly Tab -->\n <kendo-tabstrip-tab [title]=\"'Hourly'\" [selected]=\"selectedTabIndex() === 2\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row\">\n <kendo-label text=\"Run every\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"hourIntervalOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"hourInterval()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onHourIntervalChange($event)\">\n </kendo-dropdownlist>\n </div>\n <div class=\"form-row\">\n <kendo-label text=\"At minute\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"hourMinute()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onHourMinuteChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"hourSecond()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onHourSecondChange($event)\">\n </kendo-dropdownlist>\n <span class=\"time-label\">(mm:ss)</span>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Daily Tab -->\n <kendo-tabstrip-tab [title]=\"'Daily'\" [selected]=\"selectedTabIndex() === 3\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row time-row\">\n <kendo-label text=\"At\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"hourOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeHour()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeHourChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeMinute()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeMinuteChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeSecond()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeSecondChange($event)\">\n </kendo-dropdownlist>\n </div>\n\n <div class=\"form-row mode-row\">\n <kendo-label text=\"On\"></kendo-label>\n <div class=\"mode-buttons\">\n <button\n kendoButton\n [selected]=\"dailyMode() === 'every'\"\n [disabled]=\"disabled()\"\n (click)=\"onDailyModeChange('every')\">\n Every day\n </button>\n <button\n kendoButton\n [selected]=\"dailyMode() === 'weekdays'\"\n [disabled]=\"disabled()\"\n (click)=\"onDailyModeChange('weekdays')\">\n Weekdays\n </button>\n <button\n kendoButton\n [selected]=\"dailyMode() === 'weekends'\"\n [disabled]=\"disabled()\"\n (click)=\"onDailyModeChange('weekends')\">\n Weekends\n </button>\n <button\n kendoButton\n [selected]=\"dailyMode() === 'specific'\"\n [disabled]=\"disabled()\"\n (click)=\"onDailyModeChange('specific')\">\n Specific days\n </button>\n </div>\n </div>\n\n @if (dailyMode() === 'specific') {\n <div class=\"form-row days-row\">\n <div class=\"day-buttons\">\n @for (day of weekdayAbbreviations; track day.value) {\n <button\n kendoButton\n [selected]=\"isDaySelected(day.value)\"\n [disabled]=\"disabled()\"\n class=\"day-button\"\n (click)=\"onDayToggle(day.value)\">\n {{ day.label }}\n </button>\n }\n </div>\n </div>\n }\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Weekly Tab -->\n <kendo-tabstrip-tab [title]=\"'Weekly'\" [selected]=\"selectedTabIndex() === 4\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row time-row\">\n <kendo-label text=\"At\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"hourOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeHour()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeHourChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeMinute()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeMinuteChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeSecond()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeSecondChange($event)\">\n </kendo-dropdownlist>\n </div>\n\n <div class=\"form-row\">\n <kendo-label text=\"On days\"></kendo-label>\n <div class=\"day-buttons\">\n @for (day of weekdayAbbreviations; track day.value) {\n <button\n kendoButton\n [selected]=\"isDaySelected(day.value)\"\n [disabled]=\"disabled()\"\n class=\"day-button\"\n (click)=\"onDayToggle(day.value)\">\n {{ day.label }}\n </button>\n }\n </div>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Monthly Tab -->\n <kendo-tabstrip-tab [title]=\"'Monthly'\" [selected]=\"selectedTabIndex() === 5\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row time-row\">\n <kendo-label text=\"At\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"hourOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeHour()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeHourChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeMinute()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeMinuteChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeSecond()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeSecondChange($event)\">\n </kendo-dropdownlist>\n </div>\n\n <div class=\"form-row mode-row\">\n <div class=\"mode-buttons\">\n <button\n kendoButton\n [selected]=\"monthlyMode() === 'specific'\"\n [disabled]=\"disabled()\"\n (click)=\"onMonthlyModeChange('specific')\">\n Specific day\n </button>\n <button\n kendoButton\n [selected]=\"monthlyMode() === 'relative'\"\n [disabled]=\"disabled()\"\n (click)=\"onMonthlyModeChange('relative')\">\n Relative day\n </button>\n </div>\n </div>\n\n @if (monthlyMode() === 'specific') {\n <div class=\"form-row\">\n <kendo-label text=\"On day\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"dayOfMonthOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"dayOfMonth()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onDayOfMonthChange($event)\">\n </kendo-dropdownlist>\n <span class=\"suffix-text\">of every month</span>\n </div>\n }\n\n @if (monthlyMode() === 'relative') {\n <div class=\"form-row\">\n <kendo-label text=\"On the\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"relativeWeekOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"relativeWeek()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onRelativeWeekChange($event)\">\n </kendo-dropdownlist>\n <kendo-dropdownlist\n [data]=\"weekdayOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"relativeDay()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onRelativeDayChange($event)\">\n </kendo-dropdownlist>\n <span class=\"suffix-text\">of every month</span>\n </div>\n }\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Custom/Advanced Tab -->\n @if (mergedConfig().allowCustom) {\n <kendo-tabstrip-tab [title]=\"'Custom'\" [selected]=\"selectedTabIndex() === 6\">\n <ng-template kendoTabContent>\n <div class=\"tab-content custom-tab\">\n <div class=\"custom-fields\">\n <div class=\"custom-field\">\n <kendo-label text=\"Second\"></kendo-label>\n <kendo-textbox\n [value]=\"customSecond()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('second', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">0-59, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Minute\"></kendo-label>\n <kendo-textbox\n [value]=\"customMinute()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('minute', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">0-59, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Hour\"></kendo-label>\n <kendo-textbox\n [value]=\"customHour()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('hour', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">0-23, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Day\"></kendo-label>\n <kendo-textbox\n [value]=\"customDayOfMonth()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('dayOfMonth', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">1-31, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Month\"></kendo-label>\n <kendo-textbox\n [value]=\"customMonth()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('month', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">1-12, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Weekday\"></kendo-label>\n <kendo-textbox\n [value]=\"customDayOfWeek()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('dayOfWeek', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">0-6 (Sun-Sat)</span>\n </div>\n </div>\n\n <div class=\"syntax-reference\">\n <div class=\"reference-title\">Syntax Reference</div>\n <div class=\"reference-items\">\n <span><code>*</code> any</span>\n <span><code>*/N</code> every N</span>\n <span><code>N-M</code> range</span>\n <span><code>N,M</code> list</span>\n </div>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n }\n </kendo-tabstrip>\n\n <!-- Next Executions Section -->\n @if (mergedConfig().showNextExecutions && validationResult().isValid && nextExecutions().length > 0) {\n <div class=\"next-executions-section\">\n <div class=\"next-executions-label\">Next Executions</div>\n <ul class=\"next-executions-list\">\n @for (execution of nextExecutions(); track $index) {\n <li>{{ formatNextExecution(execution) }}</li>\n }\n </ul>\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{--cron-bg: var(--mm-cron-bg, #ffffff);--cron-border: var(--mm-cron-border, #e0e0e0);--cron-border-radius: var(--mm-cron-border-radius, 8px);--cron-text: var(--mm-cron-text, #333333);--cron-text-secondary: var(--mm-cron-text-secondary, #666666);--cron-accent: var(--mm-cron-accent, #616161);--cron-accent-hover: var(--mm-cron-accent-hover, #424242);--cron-preview-bg: var(--mm-cron-preview-bg, #f5f5f5);--cron-tab-active-bg: var(--mm-cron-tab-active-bg, #eeeeee);--cron-tab-active-border: var(--mm-cron-tab-active-border, #616161);--cron-error: var(--mm-cron-error, #d32f2f);--cron-success: var(--mm-cron-success, #388e3c);--cron-code-bg: var(--mm-cron-code-bg, #eeeeee);--cron-code-text: var(--mm-cron-code-text, #333333);--cron-shadow: var(--mm-cron-shadow, 0 2px 8px rgba(0, 0, 0, .1));--cron-font-family: var(--mm-cron-font-family, inherit);--cron-font-mono: var(--mm-cron-font-mono, \"Roboto Mono\", \"Consolas\", monospace);display:block;font-family:var(--cron-font-family)}.cron-builder{background:var(--cron-bg);border:1px solid var(--cron-border);border-radius:var(--cron-border-radius);padding:16px;box-shadow:var(--cron-shadow)}.cron-builder.disabled{opacity:.6;pointer-events:none}.expression-section{margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid var(--cron-border)}.expression-display .expression-label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--cron-text-secondary);margin-bottom:8px}.expression-display .expression-value-row{display:flex;align-items:center;gap:8px}.expression-display .expression-value{flex:1;font-family:var(--cron-font-mono);font-size:1.1rem;font-weight:500;padding:8px 12px;background:var(--cron-code-bg);color:var(--cron-code-text);border-radius:4px;letter-spacing:1px}.expression-display .expression-value.invalid{color:var(--cron-error);border:1px solid var(--cron-error)}.expression-display .copy-button{flex-shrink:0;transition:all .2s ease}.expression-display .copy-button.copied{color:var(--cron-success)!important}.expression-display .validation-error{display:flex;align-items:center;gap:6px;margin-top:8px;font-size:.85rem;color:var(--cron-error)}.expression-display .validation-error kendo-svg-icon{flex-shrink:0}.human-readable{display:flex;align-items:center;gap:8px;margin-top:12px;padding:10px 12px;background:var(--cron-preview-bg);border-radius:4px;font-size:.9rem;color:var(--cron-text)}.human-readable kendo-svg-icon{color:var(--cron-accent);flex-shrink:0}.presets-section{margin-bottom:16px}.presets-section .presets-label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--cron-text-secondary);margin-bottom:8px}.presets-section .presets-grid{display:flex;flex-wrap:wrap;gap:8px}.presets-section .preset-button{font-size:.8rem;padding:4px 12px;border-color:var(--cron-border);transition:all .2s ease}.presets-section .preset-button:hover:not(:disabled){border-color:var(--cron-accent);color:var(--cron-accent)}::ng-deep .k-tabstrip{background:transparent;border:none}::ng-deep .k-tabstrip .k-tabstrip-items-wrapper{border-bottom:1px solid var(--cron-border)}::ng-deep .k-tabstrip .k-tabstrip-items .k-item{color:var(--cron-text-secondary);background:transparent;border:none}::ng-deep .k-tabstrip .k-tabstrip-items .k-item.k-active{color:var(--cron-accent);background:var(--cron-tab-active-bg);border-bottom:2px solid var(--cron-tab-active-border)}::ng-deep .k-tabstrip .k-tabstrip-items .k-item:hover:not(.k-active){color:var(--cron-text);background:var(--cron-preview-bg)}::ng-deep .k-tabstrip .k-tabstrip-content{background:transparent;border:none;padding:16px 0}.tab-content{display:flex;flex-direction:column;gap:16px}.form-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.form-row kendo-label{min-width:80px;font-weight:500;color:var(--cron-text)}.form-row kendo-dropdownlist{min-width:140px}.form-row .separator{font-size:1.2rem;font-weight:700;color:var(--cron-text-secondary)}.form-row .time-label,.form-row .suffix-text{font-size:.85rem;color:var(--cron-text-secondary)}.mode-row .mode-buttons{display:flex;flex-wrap:wrap;gap:8px}.mode-row .mode-buttons button{transition:all .2s ease}.mode-row .mode-buttons button.k-selected,.mode-row .mode-buttons button[ng-reflect-selected=true]{background:var(--cron-accent)!important;border-color:var(--cron-accent)!important;color:#fff!important}.days-row{padding-left:92px}.day-buttons{display:flex;flex-wrap:wrap;gap:6px}.day-buttons .day-button{min-width:48px;padding:6px 10px;font-size:.85rem;transition:all .2s ease}.day-buttons .day-button.k-selected,.day-buttons .day-button[ng-reflect-selected=true]{background:var(--cron-accent)!important;border-color:var(--cron-accent)!important;color:#fff!important}.preview-text{font-size:.85rem;color:var(--cron-text-secondary)}.preview-text code{font-family:var(--cron-font-mono);background:var(--cron-preview-bg);padding:2px 6px;border-radius:3px}.custom-tab .custom-fields{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:16px}.custom-tab .custom-field{display:flex;flex-direction:column;gap:4px}.custom-tab .custom-field kendo-label{font-size:.8rem;font-weight:600;color:var(--cron-text-secondary)}.custom-tab .custom-field kendo-textbox{font-family:var(--cron-font-mono)}.custom-tab .custom-field .field-hint{font-size:.7rem;color:var(--cron-text-secondary)}.custom-tab .syntax-reference{margin-top:16px;padding:12px;background:var(--cron-preview-bg);border-radius:4px}.custom-tab .syntax-reference .reference-title{font-size:.75rem;font-weight:600;text-transform:uppercase;color:var(--cron-text-secondary);margin-bottom:8px}.custom-tab .syntax-reference .reference-items{display:flex;flex-wrap:wrap;gap:16px;font-size:.85rem;color:var(--cron-text)}.custom-tab .syntax-reference .reference-items code{font-family:var(--cron-font-mono);background:var(--cron-code-bg);color:var(--cron-code-text);padding:2px 6px;border-radius:3px;margin-right:4px}.next-executions-section{margin-top:16px;padding-top:16px;border-top:1px solid var(--cron-border)}.next-executions-section .next-executions-label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--cron-text-secondary);margin-bottom:8px}.next-executions-section .next-executions-list{list-style:none;margin:0;padding:0}.next-executions-section .next-executions-list li{display:flex;align-items:center;gap:8px;padding:6px 0;font-size:.9rem;color:var(--cron-text);font-family:var(--cron-font-mono)}.next-executions-section .next-executions-list li:before{content:\"\\25b8\";color:var(--cron-accent);font-size:.75rem}.next-executions-section .next-executions-list li:not(:last-child){border-bottom:1px dashed var(--cron-border)}@media(max-width:600px){.cron-builder{padding:12px}.form-row{flex-direction:column;align-items:flex-start}.form-row kendo-label{min-width:auto}.days-row{padding-left:0}.time-row{flex-direction:row;flex-wrap:wrap}.time-row kendo-label{width:100%;margin-bottom:8px}.custom-tab .custom-fields{grid-template-columns:repeat(2,1fr)}}@media(max-width:400px){.custom-tab .custom-fields{grid-template-columns:1fr}.presets-grid{flex-direction:column}.presets-grid .preset-button{width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TabStripModule }, { kind: "component", type: i1$3.TabStripComponent, selector: "kendo-tabstrip", inputs: ["height", "animate", "tabAlignment", "tabPosition", "keepTabContent", "closable", "scrollable", "size", "closeIcon", "closeIconClass", "closeSVGIcon", "showContentArea"], outputs: ["tabSelect", "tabClose", "tabScroll"], exportAs: ["kendoTabStrip"] }, { kind: "component", type: i1$3.TabStripTabComponent, selector: "kendo-tabstrip-tab", inputs: ["title", "disabled", "cssClass", "cssStyle", "selected", "closable", "closeIcon", "closeIconClass", "closeSVGIcon"], exportAs: ["kendoTabStripTab"] }, { kind: "directive", type: i1$3.TabContentDirective, selector: "[kendoTabContent]" }, { kind: "ngmodule", type: DropDownListModule }, { kind: "component", type: i3$1.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "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: "ngmodule", type: ButtonModule }, { kind: "component", type: i4.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: ButtonGroupModule }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i5.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: "ngmodule", type: LabelModule }, { kind: "component", type: i6.LabelComponent, selector: "kendo-label", inputs: ["text", "for", "optional", "labelCssStyle", "labelCssClass"], exportAs: ["kendoLabel"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i3.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon", "variant"], exportAs: ["kendoSVGIcon"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
7573
|
+
], ngImport: i0, template: "<div class=\"cron-builder\" [class.disabled]=\"disabled()\">\n <!-- Expression Preview Section -->\n <div class=\"expression-section\">\n <div class=\"expression-display\">\n <div class=\"expression-label\">Cron Expression</div>\n <div class=\"expression-value-row\">\n <code class=\"expression-value\" [class.invalid]=\"!validationResult().isValid\">\n {{ expression() }}\n </code>\n @if (mergedConfig().showCopyButton) {\n <button\n kendoButton\n [svgIcon]=\"copiedRecently() ? checkIcon : copyIcon\"\n [title]=\"copiedRecently() ? 'Copied!' : 'Copy to clipboard'\"\n fillMode=\"flat\"\n class=\"copy-button\"\n [class.copied]=\"copiedRecently()\"\n (click)=\"onCopyExpression()\"\n [disabled]=\"disabled()\">\n </button>\n }\n </div>\n @if (!validationResult().isValid) {\n <div class=\"validation-error\">\n <kendo-svg-icon [icon]=\"xIcon\" size=\"small\"></kendo-svg-icon>\n {{ validationResult().error }}\n </div>\n }\n </div>\n\n @if (mergedConfig().showHumanReadable && validationResult().isValid) {\n <div class=\"human-readable\">\n <kendo-svg-icon [icon]=\"clockIcon\" size=\"small\"></kendo-svg-icon>\n <span>{{ humanReadable() }}</span>\n </div>\n }\n </div>\n\n <!-- Preset Buttons -->\n @if (mergedConfig().showPresets) {\n <div class=\"presets-section\">\n <div class=\"presets-label\">Quick Select</div>\n <div class=\"presets-grid\">\n @for (preset of presetsByCategory().frequent; track preset.id) {\n <button\n kendoButton\n size=\"small\"\n fillMode=\"outline\"\n class=\"preset-button\"\n [title]=\"preset.description\"\n [disabled]=\"disabled()\"\n (click)=\"onPresetSelect(preset)\">\n {{ preset.label }}\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Schedule Type Tabs -->\n <kendo-tabstrip\n [animate]=\"false\"\n [tabAlignment]=\"'start'\"\n (tabSelect)=\"onTabSelect($event.index)\">\n\n <!-- Seconds Tab -->\n <kendo-tabstrip-tab [title]=\"'Seconds'\" [selected]=\"selectedTabIndex() === 0\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row\">\n <kendo-label text=\"Run every\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"secondIntervalOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"secondInterval()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onSecondIntervalChange($event)\">\n </kendo-dropdownlist>\n </div>\n <div class=\"preview-text\">\n Generates: <code>*/{{ secondInterval() }} * * * * *</code>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Minutes Tab -->\n <kendo-tabstrip-tab [title]=\"'Minutes'\" [selected]=\"selectedTabIndex() === 1\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row\">\n <kendo-label text=\"Run every\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"minuteIntervalOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"minuteInterval()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onMinuteIntervalChange($event)\">\n </kendo-dropdownlist>\n </div>\n <div class=\"preview-text\">\n Generates: <code>0 */{{ minuteInterval() }} * * * *</code>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Hourly Tab -->\n <kendo-tabstrip-tab [title]=\"'Hourly'\" [selected]=\"selectedTabIndex() === 2\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row\">\n <kendo-label text=\"Run every\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"hourIntervalOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"hourInterval()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onHourIntervalChange($event)\">\n </kendo-dropdownlist>\n </div>\n <div class=\"form-row\">\n <kendo-label text=\"At minute\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"hourMinute()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onHourMinuteChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"hourSecond()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onHourSecondChange($event)\">\n </kendo-dropdownlist>\n <span class=\"time-label\">(mm:ss)</span>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Daily Tab -->\n <kendo-tabstrip-tab [title]=\"'Daily'\" [selected]=\"selectedTabIndex() === 3\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row time-row\">\n <kendo-label text=\"At\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"hourOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeHour()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeHourChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeMinute()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeMinuteChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeSecond()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeSecondChange($event)\">\n </kendo-dropdownlist>\n </div>\n\n <div class=\"form-row mode-row\">\n <kendo-label text=\"On\"></kendo-label>\n <div class=\"mode-buttons\">\n <button\n kendoButton\n [selected]=\"dailyMode() === 'every'\"\n [disabled]=\"disabled()\"\n (click)=\"onDailyModeChange('every')\">\n Every day\n </button>\n <button\n kendoButton\n [selected]=\"dailyMode() === 'weekdays'\"\n [disabled]=\"disabled()\"\n (click)=\"onDailyModeChange('weekdays')\">\n Weekdays\n </button>\n <button\n kendoButton\n [selected]=\"dailyMode() === 'weekends'\"\n [disabled]=\"disabled()\"\n (click)=\"onDailyModeChange('weekends')\">\n Weekends\n </button>\n <button\n kendoButton\n [selected]=\"dailyMode() === 'specific'\"\n [disabled]=\"disabled()\"\n (click)=\"onDailyModeChange('specific')\">\n Specific days\n </button>\n </div>\n </div>\n\n @if (dailyMode() === 'specific') {\n <div class=\"form-row days-row\">\n <div class=\"day-buttons\">\n @for (day of weekdayAbbreviations; track day.value) {\n <button\n kendoButton\n [selected]=\"isDaySelected(day.value)\"\n [disabled]=\"disabled()\"\n class=\"day-button\"\n (click)=\"onDayToggle(day.value)\">\n {{ day.label }}\n </button>\n }\n </div>\n </div>\n }\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Weekly Tab -->\n <kendo-tabstrip-tab [title]=\"'Weekly'\" [selected]=\"selectedTabIndex() === 4\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row time-row\">\n <kendo-label text=\"At\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"hourOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeHour()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeHourChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeMinute()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeMinuteChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeSecond()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeSecondChange($event)\">\n </kendo-dropdownlist>\n </div>\n\n <div class=\"form-row\">\n <kendo-label text=\"On days\"></kendo-label>\n <div class=\"day-buttons\">\n @for (day of weekdayAbbreviations; track day.value) {\n <button\n kendoButton\n [selected]=\"isDaySelected(day.value)\"\n [disabled]=\"disabled()\"\n class=\"day-button\"\n (click)=\"onDayToggle(day.value)\">\n {{ day.label }}\n </button>\n }\n </div>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Monthly Tab -->\n <kendo-tabstrip-tab [title]=\"'Monthly'\" [selected]=\"selectedTabIndex() === 5\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row time-row\">\n <kendo-label text=\"At\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"hourOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeHour()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeHourChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeMinute()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeMinuteChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeSecond()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeSecondChange($event)\">\n </kendo-dropdownlist>\n </div>\n\n <div class=\"form-row mode-row\">\n <div class=\"mode-buttons\">\n <button\n kendoButton\n [selected]=\"monthlyMode() === 'specific'\"\n [disabled]=\"disabled()\"\n (click)=\"onMonthlyModeChange('specific')\">\n Specific day\n </button>\n <button\n kendoButton\n [selected]=\"monthlyMode() === 'relative'\"\n [disabled]=\"disabled()\"\n (click)=\"onMonthlyModeChange('relative')\">\n Relative day\n </button>\n </div>\n </div>\n\n @if (monthlyMode() === 'specific') {\n <div class=\"form-row\">\n <kendo-label text=\"On day\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"dayOfMonthOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"dayOfMonth()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onDayOfMonthChange($event)\">\n </kendo-dropdownlist>\n <span class=\"suffix-text\">of every month</span>\n </div>\n }\n\n @if (monthlyMode() === 'relative') {\n <div class=\"form-row\">\n <kendo-label text=\"On the\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"relativeWeekOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"relativeWeek()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onRelativeWeekChange($event)\">\n </kendo-dropdownlist>\n <kendo-dropdownlist\n [data]=\"weekdayOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"relativeDay()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onRelativeDayChange($event)\">\n </kendo-dropdownlist>\n <span class=\"suffix-text\">of every month</span>\n </div>\n }\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Custom/Advanced Tab -->\n @if (mergedConfig().allowCustom) {\n <kendo-tabstrip-tab [title]=\"'Custom'\" [selected]=\"selectedTabIndex() === 6\">\n <ng-template kendoTabContent>\n <div class=\"tab-content custom-tab\">\n <div class=\"custom-fields\">\n <div class=\"custom-field\">\n <kendo-label text=\"Second\"></kendo-label>\n <kendo-textbox\n [value]=\"customSecond()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('second', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">0-59, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Minute\"></kendo-label>\n <kendo-textbox\n [value]=\"customMinute()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('minute', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">0-59, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Hour\"></kendo-label>\n <kendo-textbox\n [value]=\"customHour()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('hour', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">0-23, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Day\"></kendo-label>\n <kendo-textbox\n [value]=\"customDayOfMonth()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('dayOfMonth', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">1-31, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Month\"></kendo-label>\n <kendo-textbox\n [value]=\"customMonth()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('month', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">1-12, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Weekday\"></kendo-label>\n <kendo-textbox\n [value]=\"customDayOfWeek()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('dayOfWeek', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">0-6 (Sun-Sat)</span>\n </div>\n </div>\n\n <div class=\"syntax-reference\">\n <div class=\"reference-title\">Syntax Reference</div>\n <div class=\"reference-items\">\n <span><code>*</code> any</span>\n <span><code>*/N</code> every N</span>\n <span><code>N-M</code> range</span>\n <span><code>N,M</code> list</span>\n </div>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n }\n </kendo-tabstrip>\n\n <!-- Next Executions Section -->\n @if (mergedConfig().showNextExecutions && validationResult().isValid && nextExecutions().length > 0) {\n <div class=\"next-executions-section\">\n <div class=\"next-executions-label\">Next Executions</div>\n <ul class=\"next-executions-list\">\n @for (execution of nextExecutions(); track $index) {\n <li>{{ formatNextExecution(execution) }}</li>\n }\n </ul>\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{--cron-bg: var(--mm-cron-bg, #ffffff);--cron-border: var(--mm-cron-border, #e0e0e0);--cron-border-radius: var(--mm-cron-border-radius, 8px);--cron-text: var(--mm-cron-text, #333333);--cron-text-secondary: var(--mm-cron-text-secondary, #666666);--cron-accent: var(--mm-cron-accent, #616161);--cron-accent-hover: var(--mm-cron-accent-hover, #424242);--cron-preview-bg: var(--mm-cron-preview-bg, #f5f5f5);--cron-tab-active-bg: var(--mm-cron-tab-active-bg, #eeeeee);--cron-tab-active-border: var(--mm-cron-tab-active-border, #616161);--cron-error: var(--mm-cron-error, #d32f2f);--cron-success: var(--mm-cron-success, #388e3c);--cron-code-bg: var(--mm-cron-code-bg, #eeeeee);--cron-code-text: var(--mm-cron-code-text, #333333);--cron-shadow: var(--mm-cron-shadow, 0 2px 8px rgba(0, 0, 0, .1));--cron-font-family: var(--mm-cron-font-family, inherit);--cron-font-mono: var(--mm-cron-font-mono, \"Roboto Mono\", \"Consolas\", monospace);display:block;font-family:var(--cron-font-family)}.cron-builder{background:var(--cron-bg);border:1px solid var(--cron-border);border-radius:var(--cron-border-radius);padding:16px;box-shadow:var(--cron-shadow)}.cron-builder.disabled{opacity:.6;pointer-events:none}.expression-section{margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid var(--cron-border)}.expression-display .expression-label{font-size:.75rem;font-weight:600;color:var(--cron-text-secondary);margin-bottom:8px}.expression-display .expression-value-row{display:flex;align-items:center;gap:8px}.expression-display .expression-value{flex:1;font-family:var(--cron-font-mono);font-size:1.1rem;font-weight:500;padding:8px 12px;background:var(--cron-code-bg);color:var(--cron-code-text);border-radius:4px}.expression-display .expression-value.invalid{color:var(--cron-error);border:1px solid var(--cron-error)}.expression-display .copy-button{flex-shrink:0;transition:all .2s ease}.expression-display .copy-button.copied{color:var(--cron-success)!important}.expression-display .validation-error{display:flex;align-items:center;gap:6px;margin-top:8px;font-size:.85rem;color:var(--cron-error)}.expression-display .validation-error kendo-svg-icon{flex-shrink:0}.human-readable{display:flex;align-items:center;gap:8px;margin-top:12px;padding:10px 12px;background:var(--cron-preview-bg);border-radius:4px;font-size:.9rem;color:var(--cron-text)}.human-readable kendo-svg-icon{color:var(--cron-accent);flex-shrink:0}.presets-section{margin-bottom:16px}.presets-section .presets-label{font-size:.75rem;font-weight:600;color:var(--cron-text-secondary);margin-bottom:8px}.presets-section .presets-grid{display:flex;flex-wrap:wrap;gap:8px}.presets-section .preset-button{font-size:.8rem;padding:4px 12px;border-color:var(--cron-border);transition:all .2s ease}.presets-section .preset-button:hover:not(:disabled){border-color:var(--cron-accent);color:var(--cron-accent)}::ng-deep .k-tabstrip{background:transparent;border:none}::ng-deep .k-tabstrip .k-tabstrip-items-wrapper{border-bottom:1px solid var(--cron-border)}::ng-deep .k-tabstrip .k-tabstrip-items .k-item{color:var(--cron-text-secondary);background:transparent;border:none}::ng-deep .k-tabstrip .k-tabstrip-items .k-item.k-active{color:var(--cron-accent);background:var(--cron-tab-active-bg);border-bottom:2px solid var(--cron-tab-active-border)}::ng-deep .k-tabstrip .k-tabstrip-items .k-item:hover:not(.k-active){color:var(--cron-text);background:var(--cron-preview-bg)}::ng-deep .k-tabstrip .k-tabstrip-content{background:transparent;border:none;padding:16px 0}.tab-content{display:flex;flex-direction:column;gap:16px}.form-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.form-row kendo-label{min-width:80px;font-weight:500;color:var(--cron-text)}.form-row kendo-dropdownlist{min-width:140px}.form-row .separator{font-size:1.2rem;font-weight:700;color:var(--cron-text-secondary)}.form-row .time-label,.form-row .suffix-text{font-size:.85rem;color:var(--cron-text-secondary)}.mode-row .mode-buttons{display:flex;flex-wrap:wrap;gap:8px}.mode-row .mode-buttons button{transition:all .2s ease}.mode-row .mode-buttons button.k-selected,.mode-row .mode-buttons button[ng-reflect-selected=true]{background:var(--cron-accent)!important;border-color:var(--cron-accent)!important;color:#fff!important}.days-row{padding-left:92px}.day-buttons{display:flex;flex-wrap:wrap;gap:6px}.day-buttons .day-button{min-width:48px;padding:6px 10px;font-size:.85rem;transition:all .2s ease}.day-buttons .day-button.k-selected,.day-buttons .day-button[ng-reflect-selected=true]{background:var(--cron-accent)!important;border-color:var(--cron-accent)!important;color:#fff!important}.preview-text{font-size:.85rem;color:var(--cron-text-secondary)}.preview-text code{font-family:var(--cron-font-mono);background:var(--cron-preview-bg);padding:2px 6px;border-radius:3px}.custom-tab .custom-fields{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:16px}.custom-tab .custom-field{display:flex;flex-direction:column;gap:4px}.custom-tab .custom-field kendo-label{font-size:.8rem;font-weight:600;color:var(--cron-text-secondary)}.custom-tab .custom-field kendo-textbox{font-family:var(--cron-font-mono)}.custom-tab .custom-field .field-hint{font-size:.7rem;color:var(--cron-text-secondary)}.custom-tab .syntax-reference{margin-top:16px;padding:12px;background:var(--cron-preview-bg);border-radius:4px}.custom-tab .syntax-reference .reference-title{font-size:.75rem;font-weight:600;color:var(--cron-text-secondary);margin-bottom:8px}.custom-tab .syntax-reference .reference-items{display:flex;flex-wrap:wrap;gap:16px;font-size:.85rem;color:var(--cron-text)}.custom-tab .syntax-reference .reference-items code{font-family:var(--cron-font-mono);background:var(--cron-code-bg);color:var(--cron-code-text);padding:2px 6px;border-radius:3px;margin-right:4px}.next-executions-section{margin-top:16px;padding-top:16px;border-top:1px solid var(--cron-border)}.next-executions-section .next-executions-label{font-size:.75rem;font-weight:600;color:var(--cron-text-secondary);margin-bottom:8px}.next-executions-section .next-executions-list{list-style:none;margin:0;padding:0}.next-executions-section .next-executions-list li{display:flex;align-items:center;gap:8px;padding:6px 0;font-size:.9rem;color:var(--cron-text);font-family:var(--cron-font-mono)}.next-executions-section .next-executions-list li:before{content:\"\\25b8\";color:var(--cron-accent);font-size:.75rem}.next-executions-section .next-executions-list li:not(:last-child){border-bottom:1px dashed var(--cron-border)}@media(max-width:600px){.cron-builder{padding:12px}.form-row{flex-direction:column;align-items:flex-start}.form-row kendo-label{min-width:auto}.days-row{padding-left:0}.time-row{flex-direction:row;flex-wrap:wrap}.time-row kendo-label{width:100%;margin-bottom:8px}.custom-tab .custom-fields{grid-template-columns:repeat(2,1fr)}}@media(max-width:400px){.custom-tab .custom-fields{grid-template-columns:1fr}.presets-grid{flex-direction:column}.presets-grid .preset-button{width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TabStripModule }, { kind: "component", type: i1$4.TabStripComponent, selector: "kendo-tabstrip", inputs: ["height", "animate", "tabAlignment", "tabPosition", "keepTabContent", "closable", "scrollable", "size", "closeIcon", "closeIconClass", "closeSVGIcon", "showContentArea"], outputs: ["tabSelect", "tabClose", "tabScroll"], exportAs: ["kendoTabStrip"] }, { kind: "component", type: i1$4.TabStripTabComponent, selector: "kendo-tabstrip-tab", inputs: ["title", "disabled", "cssClass", "cssStyle", "selected", "closable", "closeIcon", "closeIconClass", "closeSVGIcon"], exportAs: ["kendoTabStripTab"] }, { kind: "directive", type: i1$4.TabContentDirective, selector: "[kendoTabContent]" }, { kind: "ngmodule", type: DropDownListModule }, { kind: "component", type: i3.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "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: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: ButtonGroupModule }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i5.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: "ngmodule", type: LabelModule }, { kind: "component", type: i6.LabelComponent, selector: "kendo-label", inputs: ["text", "for", "optional", "labelCssStyle", "labelCssClass"], exportAs: ["kendoLabel"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i2.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon", "variant"], exportAs: ["kendoSVGIcon"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
6401
7574
|
}
|
|
6402
7575
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: CronBuilderComponent, decorators: [{
|
|
6403
7576
|
type: Component,
|
|
@@ -6417,7 +7590,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
6417
7590
|
useExisting: forwardRef(() => CronBuilderComponent),
|
|
6418
7591
|
multi: true
|
|
6419
7592
|
}
|
|
6420
|
-
], template: "<div class=\"cron-builder\" [class.disabled]=\"disabled()\">\n <!-- Expression Preview Section -->\n <div class=\"expression-section\">\n <div class=\"expression-display\">\n <div class=\"expression-label\">Cron Expression</div>\n <div class=\"expression-value-row\">\n <code class=\"expression-value\" [class.invalid]=\"!validationResult().isValid\">\n {{ expression() }}\n </code>\n @if (mergedConfig().showCopyButton) {\n <button\n kendoButton\n [svgIcon]=\"copiedRecently() ? checkIcon : copyIcon\"\n [title]=\"copiedRecently() ? 'Copied!' : 'Copy to clipboard'\"\n fillMode=\"flat\"\n class=\"copy-button\"\n [class.copied]=\"copiedRecently()\"\n (click)=\"onCopyExpression()\"\n [disabled]=\"disabled()\">\n </button>\n }\n </div>\n @if (!validationResult().isValid) {\n <div class=\"validation-error\">\n <kendo-svg-icon [icon]=\"xIcon\" size=\"small\"></kendo-svg-icon>\n {{ validationResult().error }}\n </div>\n }\n </div>\n\n @if (mergedConfig().showHumanReadable && validationResult().isValid) {\n <div class=\"human-readable\">\n <kendo-svg-icon [icon]=\"clockIcon\" size=\"small\"></kendo-svg-icon>\n <span>{{ humanReadable() }}</span>\n </div>\n }\n </div>\n\n <!-- Preset Buttons -->\n @if (mergedConfig().showPresets) {\n <div class=\"presets-section\">\n <div class=\"presets-label\">Quick Select</div>\n <div class=\"presets-grid\">\n @for (preset of presetsByCategory().frequent; track preset.id) {\n <button\n kendoButton\n size=\"small\"\n fillMode=\"outline\"\n class=\"preset-button\"\n [title]=\"preset.description\"\n [disabled]=\"disabled()\"\n (click)=\"onPresetSelect(preset)\">\n {{ preset.label }}\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Schedule Type Tabs -->\n <kendo-tabstrip\n [animate]=\"false\"\n [tabAlignment]=\"'start'\"\n (tabSelect)=\"onTabSelect($event.index)\">\n\n <!-- Seconds Tab -->\n <kendo-tabstrip-tab [title]=\"'Seconds'\" [selected]=\"selectedTabIndex() === 0\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row\">\n <kendo-label text=\"Run every\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"secondIntervalOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"secondInterval()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onSecondIntervalChange($event)\">\n </kendo-dropdownlist>\n </div>\n <div class=\"preview-text\">\n Generates: <code>*/{{ secondInterval() }} * * * * *</code>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Minutes Tab -->\n <kendo-tabstrip-tab [title]=\"'Minutes'\" [selected]=\"selectedTabIndex() === 1\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row\">\n <kendo-label text=\"Run every\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"minuteIntervalOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"minuteInterval()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onMinuteIntervalChange($event)\">\n </kendo-dropdownlist>\n </div>\n <div class=\"preview-text\">\n Generates: <code>0 */{{ minuteInterval() }} * * * *</code>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Hourly Tab -->\n <kendo-tabstrip-tab [title]=\"'Hourly'\" [selected]=\"selectedTabIndex() === 2\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row\">\n <kendo-label text=\"Run every\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"hourIntervalOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"hourInterval()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onHourIntervalChange($event)\">\n </kendo-dropdownlist>\n </div>\n <div class=\"form-row\">\n <kendo-label text=\"At minute\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"hourMinute()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onHourMinuteChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"hourSecond()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onHourSecondChange($event)\">\n </kendo-dropdownlist>\n <span class=\"time-label\">(mm:ss)</span>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Daily Tab -->\n <kendo-tabstrip-tab [title]=\"'Daily'\" [selected]=\"selectedTabIndex() === 3\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row time-row\">\n <kendo-label text=\"At\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"hourOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeHour()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeHourChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeMinute()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeMinuteChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeSecond()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeSecondChange($event)\">\n </kendo-dropdownlist>\n </div>\n\n <div class=\"form-row mode-row\">\n <kendo-label text=\"On\"></kendo-label>\n <div class=\"mode-buttons\">\n <button\n kendoButton\n [selected]=\"dailyMode() === 'every'\"\n [disabled]=\"disabled()\"\n (click)=\"onDailyModeChange('every')\">\n Every day\n </button>\n <button\n kendoButton\n [selected]=\"dailyMode() === 'weekdays'\"\n [disabled]=\"disabled()\"\n (click)=\"onDailyModeChange('weekdays')\">\n Weekdays\n </button>\n <button\n kendoButton\n [selected]=\"dailyMode() === 'weekends'\"\n [disabled]=\"disabled()\"\n (click)=\"onDailyModeChange('weekends')\">\n Weekends\n </button>\n <button\n kendoButton\n [selected]=\"dailyMode() === 'specific'\"\n [disabled]=\"disabled()\"\n (click)=\"onDailyModeChange('specific')\">\n Specific days\n </button>\n </div>\n </div>\n\n @if (dailyMode() === 'specific') {\n <div class=\"form-row days-row\">\n <div class=\"day-buttons\">\n @for (day of weekdayAbbreviations; track day.value) {\n <button\n kendoButton\n [selected]=\"isDaySelected(day.value)\"\n [disabled]=\"disabled()\"\n class=\"day-button\"\n (click)=\"onDayToggle(day.value)\">\n {{ day.label }}\n </button>\n }\n </div>\n </div>\n }\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Weekly Tab -->\n <kendo-tabstrip-tab [title]=\"'Weekly'\" [selected]=\"selectedTabIndex() === 4\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row time-row\">\n <kendo-label text=\"At\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"hourOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeHour()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeHourChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeMinute()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeMinuteChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeSecond()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeSecondChange($event)\">\n </kendo-dropdownlist>\n </div>\n\n <div class=\"form-row\">\n <kendo-label text=\"On days\"></kendo-label>\n <div class=\"day-buttons\">\n @for (day of weekdayAbbreviations; track day.value) {\n <button\n kendoButton\n [selected]=\"isDaySelected(day.value)\"\n [disabled]=\"disabled()\"\n class=\"day-button\"\n (click)=\"onDayToggle(day.value)\">\n {{ day.label }}\n </button>\n }\n </div>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Monthly Tab -->\n <kendo-tabstrip-tab [title]=\"'Monthly'\" [selected]=\"selectedTabIndex() === 5\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row time-row\">\n <kendo-label text=\"At\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"hourOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeHour()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeHourChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeMinute()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeMinuteChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeSecond()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeSecondChange($event)\">\n </kendo-dropdownlist>\n </div>\n\n <div class=\"form-row mode-row\">\n <div class=\"mode-buttons\">\n <button\n kendoButton\n [selected]=\"monthlyMode() === 'specific'\"\n [disabled]=\"disabled()\"\n (click)=\"onMonthlyModeChange('specific')\">\n Specific day\n </button>\n <button\n kendoButton\n [selected]=\"monthlyMode() === 'relative'\"\n [disabled]=\"disabled()\"\n (click)=\"onMonthlyModeChange('relative')\">\n Relative day\n </button>\n </div>\n </div>\n\n @if (monthlyMode() === 'specific') {\n <div class=\"form-row\">\n <kendo-label text=\"On day\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"dayOfMonthOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"dayOfMonth()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onDayOfMonthChange($event)\">\n </kendo-dropdownlist>\n <span class=\"suffix-text\">of every month</span>\n </div>\n }\n\n @if (monthlyMode() === 'relative') {\n <div class=\"form-row\">\n <kendo-label text=\"On the\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"relativeWeekOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"relativeWeek()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onRelativeWeekChange($event)\">\n </kendo-dropdownlist>\n <kendo-dropdownlist\n [data]=\"weekdayOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"relativeDay()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onRelativeDayChange($event)\">\n </kendo-dropdownlist>\n <span class=\"suffix-text\">of every month</span>\n </div>\n }\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Custom/Advanced Tab -->\n @if (mergedConfig().allowCustom) {\n <kendo-tabstrip-tab [title]=\"'Custom'\" [selected]=\"selectedTabIndex() === 6\">\n <ng-template kendoTabContent>\n <div class=\"tab-content custom-tab\">\n <div class=\"custom-fields\">\n <div class=\"custom-field\">\n <kendo-label text=\"Second\"></kendo-label>\n <kendo-textbox\n [value]=\"customSecond()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('second', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">0-59, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Minute\"></kendo-label>\n <kendo-textbox\n [value]=\"customMinute()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('minute', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">0-59, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Hour\"></kendo-label>\n <kendo-textbox\n [value]=\"customHour()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('hour', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">0-23, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Day\"></kendo-label>\n <kendo-textbox\n [value]=\"customDayOfMonth()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('dayOfMonth', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">1-31, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Month\"></kendo-label>\n <kendo-textbox\n [value]=\"customMonth()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('month', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">1-12, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Weekday\"></kendo-label>\n <kendo-textbox\n [value]=\"customDayOfWeek()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('dayOfWeek', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">0-6 (Sun-Sat)</span>\n </div>\n </div>\n\n <div class=\"syntax-reference\">\n <div class=\"reference-title\">Syntax Reference</div>\n <div class=\"reference-items\">\n <span><code>*</code> any</span>\n <span><code>*/N</code> every N</span>\n <span><code>N-M</code> range</span>\n <span><code>N,M</code> list</span>\n </div>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n }\n </kendo-tabstrip>\n\n <!-- Next Executions Section -->\n @if (mergedConfig().showNextExecutions && validationResult().isValid && nextExecutions().length > 0) {\n <div class=\"next-executions-section\">\n <div class=\"next-executions-label\">Next Executions</div>\n <ul class=\"next-executions-list\">\n @for (execution of nextExecutions(); track $index) {\n <li>{{ formatNextExecution(execution) }}</li>\n }\n </ul>\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{--cron-bg: var(--mm-cron-bg, #ffffff);--cron-border: var(--mm-cron-border, #e0e0e0);--cron-border-radius: var(--mm-cron-border-radius, 8px);--cron-text: var(--mm-cron-text, #333333);--cron-text-secondary: var(--mm-cron-text-secondary, #666666);--cron-accent: var(--mm-cron-accent, #616161);--cron-accent-hover: var(--mm-cron-accent-hover, #424242);--cron-preview-bg: var(--mm-cron-preview-bg, #f5f5f5);--cron-tab-active-bg: var(--mm-cron-tab-active-bg, #eeeeee);--cron-tab-active-border: var(--mm-cron-tab-active-border, #616161);--cron-error: var(--mm-cron-error, #d32f2f);--cron-success: var(--mm-cron-success, #388e3c);--cron-code-bg: var(--mm-cron-code-bg, #eeeeee);--cron-code-text: var(--mm-cron-code-text, #333333);--cron-shadow: var(--mm-cron-shadow, 0 2px 8px rgba(0, 0, 0, .1));--cron-font-family: var(--mm-cron-font-family, inherit);--cron-font-mono: var(--mm-cron-font-mono, \"Roboto Mono\", \"Consolas\", monospace);display:block;font-family:var(--cron-font-family)}.cron-builder{background:var(--cron-bg);border:1px solid var(--cron-border);border-radius:var(--cron-border-radius);padding:16px;box-shadow:var(--cron-shadow)}.cron-builder.disabled{opacity:.6;pointer-events:none}.expression-section{margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid var(--cron-border)}.expression-display .expression-label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--cron-text-secondary);margin-bottom:8px}.expression-display .expression-value-row{display:flex;align-items:center;gap:8px}.expression-display .expression-value{flex:1;font-family:var(--cron-font-mono);font-size:1.1rem;font-weight:500;padding:8px 12px;background:var(--cron-code-bg);color:var(--cron-code-text);border-radius:4px;letter-spacing:1px}.expression-display .expression-value.invalid{color:var(--cron-error);border:1px solid var(--cron-error)}.expression-display .copy-button{flex-shrink:0;transition:all .2s ease}.expression-display .copy-button.copied{color:var(--cron-success)!important}.expression-display .validation-error{display:flex;align-items:center;gap:6px;margin-top:8px;font-size:.85rem;color:var(--cron-error)}.expression-display .validation-error kendo-svg-icon{flex-shrink:0}.human-readable{display:flex;align-items:center;gap:8px;margin-top:12px;padding:10px 12px;background:var(--cron-preview-bg);border-radius:4px;font-size:.9rem;color:var(--cron-text)}.human-readable kendo-svg-icon{color:var(--cron-accent);flex-shrink:0}.presets-section{margin-bottom:16px}.presets-section .presets-label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--cron-text-secondary);margin-bottom:8px}.presets-section .presets-grid{display:flex;flex-wrap:wrap;gap:8px}.presets-section .preset-button{font-size:.8rem;padding:4px 12px;border-color:var(--cron-border);transition:all .2s ease}.presets-section .preset-button:hover:not(:disabled){border-color:var(--cron-accent);color:var(--cron-accent)}::ng-deep .k-tabstrip{background:transparent;border:none}::ng-deep .k-tabstrip .k-tabstrip-items-wrapper{border-bottom:1px solid var(--cron-border)}::ng-deep .k-tabstrip .k-tabstrip-items .k-item{color:var(--cron-text-secondary);background:transparent;border:none}::ng-deep .k-tabstrip .k-tabstrip-items .k-item.k-active{color:var(--cron-accent);background:var(--cron-tab-active-bg);border-bottom:2px solid var(--cron-tab-active-border)}::ng-deep .k-tabstrip .k-tabstrip-items .k-item:hover:not(.k-active){color:var(--cron-text);background:var(--cron-preview-bg)}::ng-deep .k-tabstrip .k-tabstrip-content{background:transparent;border:none;padding:16px 0}.tab-content{display:flex;flex-direction:column;gap:16px}.form-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.form-row kendo-label{min-width:80px;font-weight:500;color:var(--cron-text)}.form-row kendo-dropdownlist{min-width:140px}.form-row .separator{font-size:1.2rem;font-weight:700;color:var(--cron-text-secondary)}.form-row .time-label,.form-row .suffix-text{font-size:.85rem;color:var(--cron-text-secondary)}.mode-row .mode-buttons{display:flex;flex-wrap:wrap;gap:8px}.mode-row .mode-buttons button{transition:all .2s ease}.mode-row .mode-buttons button.k-selected,.mode-row .mode-buttons button[ng-reflect-selected=true]{background:var(--cron-accent)!important;border-color:var(--cron-accent)!important;color:#fff!important}.days-row{padding-left:92px}.day-buttons{display:flex;flex-wrap:wrap;gap:6px}.day-buttons .day-button{min-width:48px;padding:6px 10px;font-size:.85rem;transition:all .2s ease}.day-buttons .day-button.k-selected,.day-buttons .day-button[ng-reflect-selected=true]{background:var(--cron-accent)!important;border-color:var(--cron-accent)!important;color:#fff!important}.preview-text{font-size:.85rem;color:var(--cron-text-secondary)}.preview-text code{font-family:var(--cron-font-mono);background:var(--cron-preview-bg);padding:2px 6px;border-radius:3px}.custom-tab .custom-fields{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:16px}.custom-tab .custom-field{display:flex;flex-direction:column;gap:4px}.custom-tab .custom-field kendo-label{font-size:.8rem;font-weight:600;color:var(--cron-text-secondary)}.custom-tab .custom-field kendo-textbox{font-family:var(--cron-font-mono)}.custom-tab .custom-field .field-hint{font-size:.7rem;color:var(--cron-text-secondary)}.custom-tab .syntax-reference{margin-top:16px;padding:12px;background:var(--cron-preview-bg);border-radius:4px}.custom-tab .syntax-reference .reference-title{font-size:.75rem;font-weight:600;text-transform:uppercase;color:var(--cron-text-secondary);margin-bottom:8px}.custom-tab .syntax-reference .reference-items{display:flex;flex-wrap:wrap;gap:16px;font-size:.85rem;color:var(--cron-text)}.custom-tab .syntax-reference .reference-items code{font-family:var(--cron-font-mono);background:var(--cron-code-bg);color:var(--cron-code-text);padding:2px 6px;border-radius:3px;margin-right:4px}.next-executions-section{margin-top:16px;padding-top:16px;border-top:1px solid var(--cron-border)}.next-executions-section .next-executions-label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--cron-text-secondary);margin-bottom:8px}.next-executions-section .next-executions-list{list-style:none;margin:0;padding:0}.next-executions-section .next-executions-list li{display:flex;align-items:center;gap:8px;padding:6px 0;font-size:.9rem;color:var(--cron-text);font-family:var(--cron-font-mono)}.next-executions-section .next-executions-list li:before{content:\"\\25b8\";color:var(--cron-accent);font-size:.75rem}.next-executions-section .next-executions-list li:not(:last-child){border-bottom:1px dashed var(--cron-border)}@media(max-width:600px){.cron-builder{padding:12px}.form-row{flex-direction:column;align-items:flex-start}.form-row kendo-label{min-width:auto}.days-row{padding-left:0}.time-row{flex-direction:row;flex-wrap:wrap}.time-row kendo-label{width:100%;margin-bottom:8px}.custom-tab .custom-fields{grid-template-columns:repeat(2,1fr)}}@media(max-width:400px){.custom-tab .custom-fields{grid-template-columns:1fr}.presets-grid{flex-direction:column}.presets-grid .preset-button{width:100%}}\n"] }]
|
|
7593
|
+
], template: "<div class=\"cron-builder\" [class.disabled]=\"disabled()\">\n <!-- Expression Preview Section -->\n <div class=\"expression-section\">\n <div class=\"expression-display\">\n <div class=\"expression-label\">Cron Expression</div>\n <div class=\"expression-value-row\">\n <code class=\"expression-value\" [class.invalid]=\"!validationResult().isValid\">\n {{ expression() }}\n </code>\n @if (mergedConfig().showCopyButton) {\n <button\n kendoButton\n [svgIcon]=\"copiedRecently() ? checkIcon : copyIcon\"\n [title]=\"copiedRecently() ? 'Copied!' : 'Copy to clipboard'\"\n fillMode=\"flat\"\n class=\"copy-button\"\n [class.copied]=\"copiedRecently()\"\n (click)=\"onCopyExpression()\"\n [disabled]=\"disabled()\">\n </button>\n }\n </div>\n @if (!validationResult().isValid) {\n <div class=\"validation-error\">\n <kendo-svg-icon [icon]=\"xIcon\" size=\"small\"></kendo-svg-icon>\n {{ validationResult().error }}\n </div>\n }\n </div>\n\n @if (mergedConfig().showHumanReadable && validationResult().isValid) {\n <div class=\"human-readable\">\n <kendo-svg-icon [icon]=\"clockIcon\" size=\"small\"></kendo-svg-icon>\n <span>{{ humanReadable() }}</span>\n </div>\n }\n </div>\n\n <!-- Preset Buttons -->\n @if (mergedConfig().showPresets) {\n <div class=\"presets-section\">\n <div class=\"presets-label\">Quick Select</div>\n <div class=\"presets-grid\">\n @for (preset of presetsByCategory().frequent; track preset.id) {\n <button\n kendoButton\n size=\"small\"\n fillMode=\"outline\"\n class=\"preset-button\"\n [title]=\"preset.description\"\n [disabled]=\"disabled()\"\n (click)=\"onPresetSelect(preset)\">\n {{ preset.label }}\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Schedule Type Tabs -->\n <kendo-tabstrip\n [animate]=\"false\"\n [tabAlignment]=\"'start'\"\n (tabSelect)=\"onTabSelect($event.index)\">\n\n <!-- Seconds Tab -->\n <kendo-tabstrip-tab [title]=\"'Seconds'\" [selected]=\"selectedTabIndex() === 0\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row\">\n <kendo-label text=\"Run every\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"secondIntervalOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"secondInterval()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onSecondIntervalChange($event)\">\n </kendo-dropdownlist>\n </div>\n <div class=\"preview-text\">\n Generates: <code>*/{{ secondInterval() }} * * * * *</code>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Minutes Tab -->\n <kendo-tabstrip-tab [title]=\"'Minutes'\" [selected]=\"selectedTabIndex() === 1\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row\">\n <kendo-label text=\"Run every\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"minuteIntervalOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"minuteInterval()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onMinuteIntervalChange($event)\">\n </kendo-dropdownlist>\n </div>\n <div class=\"preview-text\">\n Generates: <code>0 */{{ minuteInterval() }} * * * *</code>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Hourly Tab -->\n <kendo-tabstrip-tab [title]=\"'Hourly'\" [selected]=\"selectedTabIndex() === 2\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row\">\n <kendo-label text=\"Run every\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"hourIntervalOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"hourInterval()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onHourIntervalChange($event)\">\n </kendo-dropdownlist>\n </div>\n <div class=\"form-row\">\n <kendo-label text=\"At minute\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"hourMinute()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onHourMinuteChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"hourSecond()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onHourSecondChange($event)\">\n </kendo-dropdownlist>\n <span class=\"time-label\">(mm:ss)</span>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Daily Tab -->\n <kendo-tabstrip-tab [title]=\"'Daily'\" [selected]=\"selectedTabIndex() === 3\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row time-row\">\n <kendo-label text=\"At\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"hourOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeHour()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeHourChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeMinute()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeMinuteChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeSecond()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeSecondChange($event)\">\n </kendo-dropdownlist>\n </div>\n\n <div class=\"form-row mode-row\">\n <kendo-label text=\"On\"></kendo-label>\n <div class=\"mode-buttons\">\n <button\n kendoButton\n [selected]=\"dailyMode() === 'every'\"\n [disabled]=\"disabled()\"\n (click)=\"onDailyModeChange('every')\">\n Every day\n </button>\n <button\n kendoButton\n [selected]=\"dailyMode() === 'weekdays'\"\n [disabled]=\"disabled()\"\n (click)=\"onDailyModeChange('weekdays')\">\n Weekdays\n </button>\n <button\n kendoButton\n [selected]=\"dailyMode() === 'weekends'\"\n [disabled]=\"disabled()\"\n (click)=\"onDailyModeChange('weekends')\">\n Weekends\n </button>\n <button\n kendoButton\n [selected]=\"dailyMode() === 'specific'\"\n [disabled]=\"disabled()\"\n (click)=\"onDailyModeChange('specific')\">\n Specific days\n </button>\n </div>\n </div>\n\n @if (dailyMode() === 'specific') {\n <div class=\"form-row days-row\">\n <div class=\"day-buttons\">\n @for (day of weekdayAbbreviations; track day.value) {\n <button\n kendoButton\n [selected]=\"isDaySelected(day.value)\"\n [disabled]=\"disabled()\"\n class=\"day-button\"\n (click)=\"onDayToggle(day.value)\">\n {{ day.label }}\n </button>\n }\n </div>\n </div>\n }\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Weekly Tab -->\n <kendo-tabstrip-tab [title]=\"'Weekly'\" [selected]=\"selectedTabIndex() === 4\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row time-row\">\n <kendo-label text=\"At\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"hourOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeHour()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeHourChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeMinute()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeMinuteChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeSecond()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeSecondChange($event)\">\n </kendo-dropdownlist>\n </div>\n\n <div class=\"form-row\">\n <kendo-label text=\"On days\"></kendo-label>\n <div class=\"day-buttons\">\n @for (day of weekdayAbbreviations; track day.value) {\n <button\n kendoButton\n [selected]=\"isDaySelected(day.value)\"\n [disabled]=\"disabled()\"\n class=\"day-button\"\n (click)=\"onDayToggle(day.value)\">\n {{ day.label }}\n </button>\n }\n </div>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Monthly Tab -->\n <kendo-tabstrip-tab [title]=\"'Monthly'\" [selected]=\"selectedTabIndex() === 5\">\n <ng-template kendoTabContent>\n <div class=\"tab-content\">\n <div class=\"form-row time-row\">\n <kendo-label text=\"At\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"hourOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeHour()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeHourChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeMinute()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeMinuteChange($event)\">\n </kendo-dropdownlist>\n <span class=\"separator\">:</span>\n <kendo-dropdownlist\n [data]=\"minuteOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"timeSecond()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onTimeSecondChange($event)\">\n </kendo-dropdownlist>\n </div>\n\n <div class=\"form-row mode-row\">\n <div class=\"mode-buttons\">\n <button\n kendoButton\n [selected]=\"monthlyMode() === 'specific'\"\n [disabled]=\"disabled()\"\n (click)=\"onMonthlyModeChange('specific')\">\n Specific day\n </button>\n <button\n kendoButton\n [selected]=\"monthlyMode() === 'relative'\"\n [disabled]=\"disabled()\"\n (click)=\"onMonthlyModeChange('relative')\">\n Relative day\n </button>\n </div>\n </div>\n\n @if (monthlyMode() === 'specific') {\n <div class=\"form-row\">\n <kendo-label text=\"On day\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"dayOfMonthOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"dayOfMonth()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onDayOfMonthChange($event)\">\n </kendo-dropdownlist>\n <span class=\"suffix-text\">of every month</span>\n </div>\n }\n\n @if (monthlyMode() === 'relative') {\n <div class=\"form-row\">\n <kendo-label text=\"On the\"></kendo-label>\n <kendo-dropdownlist\n [data]=\"relativeWeekOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"relativeWeek()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onRelativeWeekChange($event)\">\n </kendo-dropdownlist>\n <kendo-dropdownlist\n [data]=\"weekdayOptions\"\n [textField]=\"'label'\"\n [valueField]=\"'value'\"\n [value]=\"relativeDay()\"\n [valuePrimitive]=\"true\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onRelativeDayChange($event)\">\n </kendo-dropdownlist>\n <span class=\"suffix-text\">of every month</span>\n </div>\n }\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n\n <!-- Custom/Advanced Tab -->\n @if (mergedConfig().allowCustom) {\n <kendo-tabstrip-tab [title]=\"'Custom'\" [selected]=\"selectedTabIndex() === 6\">\n <ng-template kendoTabContent>\n <div class=\"tab-content custom-tab\">\n <div class=\"custom-fields\">\n <div class=\"custom-field\">\n <kendo-label text=\"Second\"></kendo-label>\n <kendo-textbox\n [value]=\"customSecond()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('second', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">0-59, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Minute\"></kendo-label>\n <kendo-textbox\n [value]=\"customMinute()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('minute', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">0-59, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Hour\"></kendo-label>\n <kendo-textbox\n [value]=\"customHour()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('hour', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">0-23, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Day\"></kendo-label>\n <kendo-textbox\n [value]=\"customDayOfMonth()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('dayOfMonth', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">1-31, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Month\"></kendo-label>\n <kendo-textbox\n [value]=\"customMonth()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('month', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">1-12, *, /</span>\n </div>\n <div class=\"custom-field\">\n <kendo-label text=\"Weekday\"></kendo-label>\n <kendo-textbox\n [value]=\"customDayOfWeek()\"\n [disabled]=\"disabled()\"\n (valueChange)=\"onCustomFieldChange('dayOfWeek', $event || '*')\">\n </kendo-textbox>\n <span class=\"field-hint\">0-6 (Sun-Sat)</span>\n </div>\n </div>\n\n <div class=\"syntax-reference\">\n <div class=\"reference-title\">Syntax Reference</div>\n <div class=\"reference-items\">\n <span><code>*</code> any</span>\n <span><code>*/N</code> every N</span>\n <span><code>N-M</code> range</span>\n <span><code>N,M</code> list</span>\n </div>\n </div>\n </div>\n </ng-template>\n </kendo-tabstrip-tab>\n }\n </kendo-tabstrip>\n\n <!-- Next Executions Section -->\n @if (mergedConfig().showNextExecutions && validationResult().isValid && nextExecutions().length > 0) {\n <div class=\"next-executions-section\">\n <div class=\"next-executions-label\">Next Executions</div>\n <ul class=\"next-executions-list\">\n @for (execution of nextExecutions(); track $index) {\n <li>{{ formatNextExecution(execution) }}</li>\n }\n </ul>\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{--cron-bg: var(--mm-cron-bg, #ffffff);--cron-border: var(--mm-cron-border, #e0e0e0);--cron-border-radius: var(--mm-cron-border-radius, 8px);--cron-text: var(--mm-cron-text, #333333);--cron-text-secondary: var(--mm-cron-text-secondary, #666666);--cron-accent: var(--mm-cron-accent, #616161);--cron-accent-hover: var(--mm-cron-accent-hover, #424242);--cron-preview-bg: var(--mm-cron-preview-bg, #f5f5f5);--cron-tab-active-bg: var(--mm-cron-tab-active-bg, #eeeeee);--cron-tab-active-border: var(--mm-cron-tab-active-border, #616161);--cron-error: var(--mm-cron-error, #d32f2f);--cron-success: var(--mm-cron-success, #388e3c);--cron-code-bg: var(--mm-cron-code-bg, #eeeeee);--cron-code-text: var(--mm-cron-code-text, #333333);--cron-shadow: var(--mm-cron-shadow, 0 2px 8px rgba(0, 0, 0, .1));--cron-font-family: var(--mm-cron-font-family, inherit);--cron-font-mono: var(--mm-cron-font-mono, \"Roboto Mono\", \"Consolas\", monospace);display:block;font-family:var(--cron-font-family)}.cron-builder{background:var(--cron-bg);border:1px solid var(--cron-border);border-radius:var(--cron-border-radius);padding:16px;box-shadow:var(--cron-shadow)}.cron-builder.disabled{opacity:.6;pointer-events:none}.expression-section{margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid var(--cron-border)}.expression-display .expression-label{font-size:.75rem;font-weight:600;color:var(--cron-text-secondary);margin-bottom:8px}.expression-display .expression-value-row{display:flex;align-items:center;gap:8px}.expression-display .expression-value{flex:1;font-family:var(--cron-font-mono);font-size:1.1rem;font-weight:500;padding:8px 12px;background:var(--cron-code-bg);color:var(--cron-code-text);border-radius:4px}.expression-display .expression-value.invalid{color:var(--cron-error);border:1px solid var(--cron-error)}.expression-display .copy-button{flex-shrink:0;transition:all .2s ease}.expression-display .copy-button.copied{color:var(--cron-success)!important}.expression-display .validation-error{display:flex;align-items:center;gap:6px;margin-top:8px;font-size:.85rem;color:var(--cron-error)}.expression-display .validation-error kendo-svg-icon{flex-shrink:0}.human-readable{display:flex;align-items:center;gap:8px;margin-top:12px;padding:10px 12px;background:var(--cron-preview-bg);border-radius:4px;font-size:.9rem;color:var(--cron-text)}.human-readable kendo-svg-icon{color:var(--cron-accent);flex-shrink:0}.presets-section{margin-bottom:16px}.presets-section .presets-label{font-size:.75rem;font-weight:600;color:var(--cron-text-secondary);margin-bottom:8px}.presets-section .presets-grid{display:flex;flex-wrap:wrap;gap:8px}.presets-section .preset-button{font-size:.8rem;padding:4px 12px;border-color:var(--cron-border);transition:all .2s ease}.presets-section .preset-button:hover:not(:disabled){border-color:var(--cron-accent);color:var(--cron-accent)}::ng-deep .k-tabstrip{background:transparent;border:none}::ng-deep .k-tabstrip .k-tabstrip-items-wrapper{border-bottom:1px solid var(--cron-border)}::ng-deep .k-tabstrip .k-tabstrip-items .k-item{color:var(--cron-text-secondary);background:transparent;border:none}::ng-deep .k-tabstrip .k-tabstrip-items .k-item.k-active{color:var(--cron-accent);background:var(--cron-tab-active-bg);border-bottom:2px solid var(--cron-tab-active-border)}::ng-deep .k-tabstrip .k-tabstrip-items .k-item:hover:not(.k-active){color:var(--cron-text);background:var(--cron-preview-bg)}::ng-deep .k-tabstrip .k-tabstrip-content{background:transparent;border:none;padding:16px 0}.tab-content{display:flex;flex-direction:column;gap:16px}.form-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.form-row kendo-label{min-width:80px;font-weight:500;color:var(--cron-text)}.form-row kendo-dropdownlist{min-width:140px}.form-row .separator{font-size:1.2rem;font-weight:700;color:var(--cron-text-secondary)}.form-row .time-label,.form-row .suffix-text{font-size:.85rem;color:var(--cron-text-secondary)}.mode-row .mode-buttons{display:flex;flex-wrap:wrap;gap:8px}.mode-row .mode-buttons button{transition:all .2s ease}.mode-row .mode-buttons button.k-selected,.mode-row .mode-buttons button[ng-reflect-selected=true]{background:var(--cron-accent)!important;border-color:var(--cron-accent)!important;color:#fff!important}.days-row{padding-left:92px}.day-buttons{display:flex;flex-wrap:wrap;gap:6px}.day-buttons .day-button{min-width:48px;padding:6px 10px;font-size:.85rem;transition:all .2s ease}.day-buttons .day-button.k-selected,.day-buttons .day-button[ng-reflect-selected=true]{background:var(--cron-accent)!important;border-color:var(--cron-accent)!important;color:#fff!important}.preview-text{font-size:.85rem;color:var(--cron-text-secondary)}.preview-text code{font-family:var(--cron-font-mono);background:var(--cron-preview-bg);padding:2px 6px;border-radius:3px}.custom-tab .custom-fields{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:16px}.custom-tab .custom-field{display:flex;flex-direction:column;gap:4px}.custom-tab .custom-field kendo-label{font-size:.8rem;font-weight:600;color:var(--cron-text-secondary)}.custom-tab .custom-field kendo-textbox{font-family:var(--cron-font-mono)}.custom-tab .custom-field .field-hint{font-size:.7rem;color:var(--cron-text-secondary)}.custom-tab .syntax-reference{margin-top:16px;padding:12px;background:var(--cron-preview-bg);border-radius:4px}.custom-tab .syntax-reference .reference-title{font-size:.75rem;font-weight:600;color:var(--cron-text-secondary);margin-bottom:8px}.custom-tab .syntax-reference .reference-items{display:flex;flex-wrap:wrap;gap:16px;font-size:.85rem;color:var(--cron-text)}.custom-tab .syntax-reference .reference-items code{font-family:var(--cron-font-mono);background:var(--cron-code-bg);color:var(--cron-code-text);padding:2px 6px;border-radius:3px;margin-right:4px}.next-executions-section{margin-top:16px;padding-top:16px;border-top:1px solid var(--cron-border)}.next-executions-section .next-executions-label{font-size:.75rem;font-weight:600;color:var(--cron-text-secondary);margin-bottom:8px}.next-executions-section .next-executions-list{list-style:none;margin:0;padding:0}.next-executions-section .next-executions-list li{display:flex;align-items:center;gap:8px;padding:6px 0;font-size:.9rem;color:var(--cron-text);font-family:var(--cron-font-mono)}.next-executions-section .next-executions-list li:before{content:\"\\25b8\";color:var(--cron-accent);font-size:.75rem}.next-executions-section .next-executions-list li:not(:last-child){border-bottom:1px dashed var(--cron-border)}@media(max-width:600px){.cron-builder{padding:12px}.form-row{flex-direction:column;align-items:flex-start}.form-row kendo-label{min-width:auto}.days-row{padding-left:0}.time-row{flex-direction:row;flex-wrap:wrap}.time-row kendo-label{width:100%;margin-bottom:8px}.custom-tab .custom-fields{grid-template-columns:repeat(2,1fr)}}@media(max-width:400px){.custom-tab .custom-fields{grid-template-columns:1fr}.presets-grid{flex-direction:column}.presets-grid .preset-button{width:100%}}\n"] }]
|
|
6421
7594
|
}], ctorParameters: () => [], propDecorators: { config: [{
|
|
6422
7595
|
type: Input
|
|
6423
7596
|
}] } });
|
|
@@ -6466,7 +7639,7 @@ class CopyableTextComponent {
|
|
|
6466
7639
|
});
|
|
6467
7640
|
}
|
|
6468
7641
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: CopyableTextComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6469
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: CopyableTextComponent, isStandalone: true, selector: "mm-copyable-text", inputs: { value: "value", label: "label", copyLabel: "copyLabel", buttonTitle: "buttonTitle" }, outputs: { copied: "copied" }, ngImport: i0, template: "<div class=\"mm-copyable-text\">\n @if (label) {\n <label class=\"label\">{{ label }}</label>\n }\n <div class=\"value-container\">\n <span class=\"value\">{{ hasValue ? value : '\u2014' }}</span>\n @if (hasValue) {\n <button\n kendoButton\n type=\"button\"\n fillMode=\"flat\"\n [svgIcon]=\"copyIcon\"\n [title]=\"buttonTitle\"\n class=\"copy-button\"\n (click)=\"copyToClipboard()\">\n </button>\n }\n </div>\n</div>\n", styles: [".mm-copyable-text{display:flex;flex-direction:column;gap:4px}.mm-copyable-text .label{font-size:.875rem;font-weight:500;color:var(--kendo-color-subtle, #6c757d)}.mm-copyable-text .value-container{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--theme-bg-elevated, rgba(31, 46, 64, .4));border:1px solid var(--theme-border-subtle, rgba(100, 206, 185, .15));border-radius:4px;transition:border-color .2s ease,box-shadow .2s ease}.mm-copyable-text .value-container:hover{border-color:var(--theme-border-hover, rgba(100, 206, 185, .3))}.mm-copyable-text .value-container .value{flex:1;font-family:Roboto Mono,Consolas,Monaco,monospace;font-size:.875rem;color:var(--theme-text-primary, rgba(var(--octo-text-color), .9));word-break:break-all;-webkit-user-select:text;user-select:text}.mm-copyable-text .value-container .copy-button{flex-shrink:0;opacity:.7;transition:opacity .2s ease}.mm-copyable-text .value-container .copy-button:hover{opacity:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type:
|
|
7642
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: CopyableTextComponent, isStandalone: true, selector: "mm-copyable-text", inputs: { value: "value", label: "label", copyLabel: "copyLabel", buttonTitle: "buttonTitle" }, outputs: { copied: "copied" }, ngImport: i0, template: "<div class=\"mm-copyable-text\">\n @if (label) {\n <label class=\"label\">{{ label }}</label>\n }\n <div class=\"value-container\">\n <span class=\"value\">{{ hasValue ? value : '\u2014' }}</span>\n @if (hasValue) {\n <button\n kendoButton\n type=\"button\"\n fillMode=\"flat\"\n [svgIcon]=\"copyIcon\"\n [title]=\"buttonTitle\"\n class=\"copy-button\"\n (click)=\"copyToClipboard()\">\n </button>\n }\n </div>\n</div>\n", styles: [".mm-copyable-text{display:flex;flex-direction:column;gap:4px}.mm-copyable-text .label{font-size:.875rem;font-weight:500;color:var(--kendo-color-subtle, #6c757d)}.mm-copyable-text .value-container{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--theme-bg-elevated, rgba(31, 46, 64, .4));border:1px solid var(--theme-border-subtle, rgba(100, 206, 185, .15));border-radius:4px;transition:border-color .2s ease,box-shadow .2s ease}.mm-copyable-text .value-container:hover{border-color:var(--theme-border-hover, rgba(100, 206, 185, .3))}.mm-copyable-text .value-container .value{flex:1;font-family:Roboto Mono,Consolas,Monaco,monospace;font-size:.875rem;color:var(--theme-text-primary, rgba(var(--octo-text-color), .9));word-break:break-all;-webkit-user-select:text;user-select:text}.mm-copyable-text .value-container .copy-button{flex-shrink:0;opacity:.7;transition:opacity .2s ease}.mm-copyable-text .value-container .copy-button:hover{opacity:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: SVGIconModule }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
6470
7643
|
}
|
|
6471
7644
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: CopyableTextComponent, decorators: [{
|
|
6472
7645
|
type: Component,
|
|
@@ -6546,6 +7719,180 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
6546
7719
|
type: Injectable
|
|
6547
7720
|
}] });
|
|
6548
7721
|
|
|
7722
|
+
const DEFAULT_ROW_ACTIONS_MESSAGES = {
|
|
7723
|
+
actionsFor: 'Actions for {name}',
|
|
7724
|
+
actions: 'Actions',
|
|
7725
|
+
moreActionsFor: 'More actions for {name}',
|
|
7726
|
+
moreActions: 'More actions',
|
|
7727
|
+
};
|
|
7728
|
+
/** App-wide translations of `mm-row-actions`. Partial: members left out keep their English default. */
|
|
7729
|
+
const MM_ROW_ACTIONS_MESSAGES = new InjectionToken('MM_ROW_ACTIONS_MESSAGES');
|
|
7730
|
+
/**
|
|
7731
|
+
* Merges overrides over {@link DEFAULT_ROW_ACTIONS_MESSAGES}, later sources winning; members that
|
|
7732
|
+
* are missing, `undefined` or `null` keep the earlier value.
|
|
7733
|
+
*/
|
|
7734
|
+
function resolveRowActionsMessages(...overrides) {
|
|
7735
|
+
const resolved = { ...DEFAULT_ROW_ACTIONS_MESSAGES };
|
|
7736
|
+
for (const messages of overrides) {
|
|
7737
|
+
if (!messages) {
|
|
7738
|
+
continue;
|
|
7739
|
+
}
|
|
7740
|
+
for (const key of Object.keys(messages)) {
|
|
7741
|
+
const value = messages[key];
|
|
7742
|
+
if (value !== undefined && value !== null) {
|
|
7743
|
+
resolved[key] = value;
|
|
7744
|
+
}
|
|
7745
|
+
}
|
|
7746
|
+
}
|
|
7747
|
+
return resolved;
|
|
7748
|
+
}
|
|
7749
|
+
|
|
7750
|
+
/**
|
|
7751
|
+
* The actions of one table row (AB#5570): icon buttons (16 px) with tooltip and an accessible
|
|
7752
|
+
* name that includes the row ("Edit Mesh Adapter"), from one shared action definition.
|
|
7753
|
+
* At most `maxInline` slots (default 3): when the actions do not fit, the last slot becomes a
|
|
7754
|
+
* "More actions for …" menu. Disabled actions stay focusable and announce their reason;
|
|
7755
|
+
* in the menu the reason is shown under the item text.
|
|
7756
|
+
*
|
|
7757
|
+
* Texts ("Actions for …", "More actions for …") come from {@link MM_ROW_ACTIONS_MESSAGES} and the
|
|
7758
|
+
* `messages` input (the input wins), English by default.
|
|
7759
|
+
*
|
|
7760
|
+
* The component only emits — destructive handlers confirm first (danger dialog naming the target).
|
|
7761
|
+
*
|
|
7762
|
+
* ```html
|
|
7763
|
+
* <td class="actions">
|
|
7764
|
+
* <mm-row-actions [actions]="rowActions(run)" [rowLabel]="runLabel(run)" (triggered)="onAction($event, run)" />
|
|
7765
|
+
* </td>
|
|
7766
|
+
* ```
|
|
7767
|
+
*/
|
|
7768
|
+
class RowActionsComponent {
|
|
7769
|
+
/** Action definitions for this row (hidden ones are filtered out). */
|
|
7770
|
+
actions = input.required(/* @ts-ignore */
|
|
7771
|
+
...(ngDevMode ? [{ debugName: "actions" }] : /* istanbul ignore next */ []));
|
|
7772
|
+
/** Human name of the row ("Mesh Adapter", "Encrypt run 2026-10-06 17:09"); part of every accessible name. */
|
|
7773
|
+
rowLabel = input.required(/* @ts-ignore */
|
|
7774
|
+
...(ngDevMode ? [{ debugName: "rowLabel" }] : /* istanbul ignore next */ []));
|
|
7775
|
+
/** Slots incl. the overflow button. */
|
|
7776
|
+
maxInline = input(MM_ROW_ACTIONS_MAX_INLINE, { ...(ngDevMode ? { debugName: "maxInline" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
7777
|
+
/** Translations for this instance; members left out come from `MM_ROW_ACTIONS_MESSAGES`, else English. */
|
|
7778
|
+
messages = input(null, /* @ts-ignore */
|
|
7779
|
+
...(ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []));
|
|
7780
|
+
/** Fires for enabled actions only, inline or from the menu. */
|
|
7781
|
+
triggered = output();
|
|
7782
|
+
router = inject(Router, { optional: true });
|
|
7783
|
+
/** Menu links resolve like the inline `routerLink`: relative to the hosting route. */
|
|
7784
|
+
route = inject(ActivatedRoute, { optional: true });
|
|
7785
|
+
injectedMessages = inject(MM_ROW_ACTIONS_MESSAGES, { optional: true });
|
|
7786
|
+
moreIcon = moreVerticalIcon;
|
|
7787
|
+
m = computed(() => resolveRowActionsMessages(this.injectedMessages, this.messages()), /* @ts-ignore */
|
|
7788
|
+
...(ngDevMode ? [{ debugName: "m" }] : /* istanbul ignore next */ []));
|
|
7789
|
+
split = computed(() => splitRowActions(this.actions(), this.maxInline()), /* @ts-ignore */
|
|
7790
|
+
...(ngDevMode ? [{ debugName: "split" }] : /* istanbul ignore next */ []));
|
|
7791
|
+
menuItems = computed(() => this.split().menu.map((action) => ({
|
|
7792
|
+
text: action.menuLabel || action.label,
|
|
7793
|
+
svgIcon: action.icon,
|
|
7794
|
+
disabled: isActionDisabled(action),
|
|
7795
|
+
action,
|
|
7796
|
+
})), /* @ts-ignore */
|
|
7797
|
+
...(ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []));
|
|
7798
|
+
/** No group semantics for an empty cell (all actions hidden). */
|
|
7799
|
+
hasActions = computed(() => this.split().inline.length > 0 || this.split().menu.length > 0, /* @ts-ignore */
|
|
7800
|
+
...(ngDevMode ? [{ debugName: "hasActions" }] : /* istanbul ignore next */ []));
|
|
7801
|
+
moreLabel = computed(() => this.format(this.m().moreActionsFor, this.m().moreActions), /* @ts-ignore */
|
|
7802
|
+
...(ngDevMode ? [{ debugName: "moreLabel" }] : /* istanbul ignore next */ []));
|
|
7803
|
+
groupLabel = computed(() => this.format(this.m().actionsFor, this.m().actions), /* @ts-ignore */
|
|
7804
|
+
...(ngDevMode ? [{ debugName: "groupLabel" }] : /* istanbul ignore next */ []));
|
|
7805
|
+
format(withName, withoutName) {
|
|
7806
|
+
const name = (this.rowLabel() ?? '').trim();
|
|
7807
|
+
return name ? withName.split('{name}').join(name) : withoutName;
|
|
7808
|
+
}
|
|
7809
|
+
onMenuItem(item) {
|
|
7810
|
+
if (!item || item.disabled) {
|
|
7811
|
+
return;
|
|
7812
|
+
}
|
|
7813
|
+
const link = item.action.link;
|
|
7814
|
+
if (link && this.router) {
|
|
7815
|
+
const commands = typeof link.commands === 'string' ? [link.commands] : [...link.commands];
|
|
7816
|
+
void this.router.navigate(commands, { queryParams: link.queryParams, relativeTo: this.route ?? undefined });
|
|
7817
|
+
}
|
|
7818
|
+
this.triggered.emit({ id: item.action.id, action: item.action });
|
|
7819
|
+
}
|
|
7820
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: RowActionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7821
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: RowActionsComponent, isStandalone: true, selector: "mm-row-actions", inputs: { actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: true, transformFunction: null }, rowLabel: { classPropertyName: "rowLabel", publicName: "rowLabel", isSignal: true, isRequired: true, transformFunction: null }, maxInline: { classPropertyName: "maxInline", publicName: "maxInline", isSignal: true, isRequired: false, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { triggered: "triggered" }, host: { properties: { "attr.role": "hasActions() ? \"group\" : null", "attr.aria-label": "hasActions() ? groupLabel() : null" }, classAttribute: "mm-row-actions" }, ngImport: i0, template: `
|
|
7822
|
+
@for (action of split().inline; track action.id) {
|
|
7823
|
+
<mm-action-button [action]="action" [targetLabel]="rowLabel()" context="row" display="icon"
|
|
7824
|
+
(triggered)="triggered.emit($event)" />
|
|
7825
|
+
}
|
|
7826
|
+
@if (menuItems().length) {
|
|
7827
|
+
<kendo-dropdownbutton
|
|
7828
|
+
class="mm-row-actions__more"
|
|
7829
|
+
[data]="menuItems()"
|
|
7830
|
+
[svgIcon]="moreIcon"
|
|
7831
|
+
size="small"
|
|
7832
|
+
fillMode="flat"
|
|
7833
|
+
themeColor="base"
|
|
7834
|
+
[buttonAttributes]="{ title: moreLabel(), 'aria-label': moreLabel(), 'data-action': 'more' }"
|
|
7835
|
+
(itemClick)="onMenuItem($event)"
|
|
7836
|
+
>
|
|
7837
|
+
<ng-template kendoDropDownButtonItemTemplate let-item>
|
|
7838
|
+
<span class="mm-row-actions__item" [class.mm-row-actions__item--danger]="!!item.action.danger"
|
|
7839
|
+
[attr.data-action]="item.action.id">
|
|
7840
|
+
@if (item.svgIcon) {
|
|
7841
|
+
<kendo-svgicon [icon]="item.svgIcon" />
|
|
7842
|
+
}
|
|
7843
|
+
<span class="mm-row-actions__item-text">
|
|
7844
|
+
<span>{{ item.text }}</span>
|
|
7845
|
+
@if (item.disabled) {
|
|
7846
|
+
<span class="mm-row-actions__item-reason">{{ item.action.disabledReason }}</span>
|
|
7847
|
+
}
|
|
7848
|
+
</span>
|
|
7849
|
+
</span>
|
|
7850
|
+
</ng-template>
|
|
7851
|
+
</kendo-dropdownbutton>
|
|
7852
|
+
}
|
|
7853
|
+
`, isInline: true, styles: [".mm-row-actions{display:inline-flex;align-items:center;gap:var(--theme-space-1, 4px)}.mm-row-actions__item{display:inline-flex;align-items:flex-start;gap:var(--theme-space-2, 8px)}.mm-row-actions__item-text{display:inline-flex;flex-direction:column}.mm-row-actions__item-reason{font-size:.85em;color:var(--theme-text-muted, #75829a);white-space:normal;max-width:280px}.mm-row-actions__item--danger{color:var(--theme-status-error, #c0385f)}.mm-row-actions__more .k-button:focus-visible{outline:none;box-shadow:var(--theme-focus-ring, 0 0 0 2px #ffffff, 0 0 0 4px #2e8473)}\n"], dependencies: [{ kind: "ngmodule", type: DropDownButtonModule }, { kind: "component", type: i1$1.DropDownButtonComponent, selector: "kendo-dropdownbutton", inputs: ["arrowIcon", "icon", "svgIcon", "iconClass", "imageUrl", "textField", "data", "size", "rounded", "fillMode", "themeColor", "buttonAttributes"], outputs: ["itemClick", "focus", "blur"], exportAs: ["kendoDropDownButton"] }, { kind: "directive", type: i1$1.ButtonItemTemplateDirective, selector: "[kendoDropDownButtonItemTemplate],[kendoSplitButtonItemTemplate]" }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i2.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon", "variant"], exportAs: ["kendoSVGIcon"] }, { kind: "component", type: ActionButtonComponent, selector: "mm-action-button", inputs: ["action", "targetLabel", "context", "display", "primary"], outputs: ["triggered"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
7854
|
+
}
|
|
7855
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: RowActionsComponent, decorators: [{
|
|
7856
|
+
type: Component,
|
|
7857
|
+
args: [{ selector: 'mm-row-actions', standalone: true, imports: [DropDownButtonModule, SVGIconModule, ActionButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
|
|
7858
|
+
class: 'mm-row-actions',
|
|
7859
|
+
'[attr.role]': 'hasActions() ? "group" : null',
|
|
7860
|
+
'[attr.aria-label]': 'hasActions() ? groupLabel() : null',
|
|
7861
|
+
}, template: `
|
|
7862
|
+
@for (action of split().inline; track action.id) {
|
|
7863
|
+
<mm-action-button [action]="action" [targetLabel]="rowLabel()" context="row" display="icon"
|
|
7864
|
+
(triggered)="triggered.emit($event)" />
|
|
7865
|
+
}
|
|
7866
|
+
@if (menuItems().length) {
|
|
7867
|
+
<kendo-dropdownbutton
|
|
7868
|
+
class="mm-row-actions__more"
|
|
7869
|
+
[data]="menuItems()"
|
|
7870
|
+
[svgIcon]="moreIcon"
|
|
7871
|
+
size="small"
|
|
7872
|
+
fillMode="flat"
|
|
7873
|
+
themeColor="base"
|
|
7874
|
+
[buttonAttributes]="{ title: moreLabel(), 'aria-label': moreLabel(), 'data-action': 'more' }"
|
|
7875
|
+
(itemClick)="onMenuItem($event)"
|
|
7876
|
+
>
|
|
7877
|
+
<ng-template kendoDropDownButtonItemTemplate let-item>
|
|
7878
|
+
<span class="mm-row-actions__item" [class.mm-row-actions__item--danger]="!!item.action.danger"
|
|
7879
|
+
[attr.data-action]="item.action.id">
|
|
7880
|
+
@if (item.svgIcon) {
|
|
7881
|
+
<kendo-svgicon [icon]="item.svgIcon" />
|
|
7882
|
+
}
|
|
7883
|
+
<span class="mm-row-actions__item-text">
|
|
7884
|
+
<span>{{ item.text }}</span>
|
|
7885
|
+
@if (item.disabled) {
|
|
7886
|
+
<span class="mm-row-actions__item-reason">{{ item.action.disabledReason }}</span>
|
|
7887
|
+
}
|
|
7888
|
+
</span>
|
|
7889
|
+
</span>
|
|
7890
|
+
</ng-template>
|
|
7891
|
+
</kendo-dropdownbutton>
|
|
7892
|
+
}
|
|
7893
|
+
`, styles: [".mm-row-actions{display:inline-flex;align-items:center;gap:var(--theme-space-1, 4px)}.mm-row-actions__item{display:inline-flex;align-items:flex-start;gap:var(--theme-space-2, 8px)}.mm-row-actions__item-text{display:inline-flex;flex-direction:column}.mm-row-actions__item-reason{font-size:.85em;color:var(--theme-text-muted, #75829a);white-space:normal;max-width:280px}.mm-row-actions__item--danger{color:var(--theme-status-error, #c0385f)}.mm-row-actions__more .k-button:focus-visible{outline:none;box-shadow:var(--theme-focus-ring, 0 0 0 2px #ffffff, 0 0 0 4px #2e8473)}\n"] }]
|
|
7894
|
+
}], propDecorators: { actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: true }] }], rowLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowLabel", required: true }] }], maxInline: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxInline", required: false }] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: false }] }], triggered: [{ type: i0.Output, args: ["triggered"] }] } });
|
|
7895
|
+
|
|
6549
7896
|
/*
|
|
6550
7897
|
* Public API Surface of shared-ui
|
|
6551
7898
|
*/
|
|
@@ -6569,5 +7916,5 @@ function provideMmSharedUi() {
|
|
|
6569
7916
|
* Generated bundle index. Do not edit.
|
|
6570
7917
|
*/
|
|
6571
7918
|
|
|
6572
|
-
export { BaseFormComponent, BaseTreeDetailComponent, ButtonTypes, BytesToSizePipe, CRON_PRESETS, ConfirmationService, ConfirmationWindowComponent, ConfirmationWindowResult, CopyableTextComponent, CronBuilderComponent, CronHumanizerService, CronParserService, DEFAULT_CONFIRMATION_WINDOW_MESSAGES, DEFAULT_CRON_BUILDER_CONFIG, DEFAULT_ENTITY_SELECT_DIALOG_MESSAGES, DEFAULT_ENTITY_SELECT_INPUT_MESSAGES, DEFAULT_INPUT_DIALOG_MESSAGES, DEFAULT_LIST_VIEW_MESSAGES, DEFAULT_MESSAGE_DETAILS_DIALOG_MESSAGES, DEFAULT_NOTIFICATION_DISPLAY_MESSAGES, DEFAULT_PROGRESS_WINDOW_MESSAGES, DEFAULT_RESPONSIVE_COLSPAN, DEFAULT_SAVE_AS_DIALOG_MESSAGES, DEFAULT_TIME_RANGE_LABELS, DataSourceBase, DataSourceTyped, DialogType, EntitySelectDialogComponent, EntitySelectDialogService, EntitySelectInputComponent, FetchResultBase, FetchResultTyped, FileUploadResult, FileUploadService, FormTitleExtraDirective, HAS_UNSAVED_CHANGES, HOUR_INTERVALS, HierarchyDataSource, HierarchyDataSourceBase, ImportStrategyDialogComponent, ImportStrategyDialogResult, ImportStrategyDialogService, ImportStrategyDto, InputDialogComponent, InputService, ListStateService, ListViewComponent, ListViewFiltersDirective, MINUTE_INTERVALS, MessageDetailsDialogComponent, MessageDetailsDialogService, MessageListenerService, MmListViewDataBindingDirective, NotificationDisplayService, PascalCasePipe, ProgressValue, ProgressWindowComponent, ProgressWindowService, RELATIVE_WEEKS, SECOND_INTERVALS, SaveAsDialogComponent, SaveAsDialogService, TimeRangePickerComponent, TimeRangeUtils, TreeComponent, UnsavedChangesDirective, UnsavedChangesGuard, UploadFileDialogComponent, WEEKDAYS, WEEKDAY_ABBREVIATIONS, WindowStateService, generateDayOfMonthOptions, generateHourOptions, generateMinuteOptions, provideMmSharedUi };
|
|
7919
|
+
export { ActionButtonComponent, BaseFormComponent, BaseTreeDetailComponent, ButtonTypes, BytesToSizePipe, CRON_PRESETS, ConfirmationService, ConfirmationWindowComponent, ConfirmationWindowResult, CopyableTextComponent, CronBuilderComponent, CronHumanizerService, CronParserService, DANGER_CONFIRM_ENVIRONMENT, DANGER_CONFIRM_FALLBACK_WORD, DANGER_CONFIRM_SAFE_FOCUS, DEFAULT_CONFIRMATION_WINDOW_MESSAGES, DEFAULT_CRON_BUILDER_CONFIG, DEFAULT_DANGER_CONFIRMATION_MESSAGES, DEFAULT_EMPTY_STATE_RETRY_LABEL, DEFAULT_ENTITY_SELECT_DIALOG_MESSAGES, DEFAULT_ENTITY_SELECT_INPUT_MESSAGES, DEFAULT_INPUT_DIALOG_MESSAGES, DEFAULT_LIST_VIEW_MESSAGES, DEFAULT_MESSAGE_DETAILS_DIALOG_MESSAGES, DEFAULT_NOTIFICATION_DISPLAY_MESSAGES, DEFAULT_PROGRESS_WINDOW_MESSAGES, DEFAULT_RESPONSIVE_COLSPAN, DEFAULT_ROW_ACTIONS_MESSAGES, DEFAULT_SAVE_AS_DIALOG_MESSAGES, DEFAULT_TIME_RANGE_LABELS, DangerConfirmationResult, DangerConfirmationWindowComponent, DataSourceBase, DataSourceTyped, DialogType, EmptyStateComponent, EntitySelectDialogComponent, EntitySelectDialogService, EntitySelectInputComponent, FetchResultBase, FetchResultTyped, FileUploadResult, FileUploadService, FormTitleExtraDirective, HAS_UNSAVED_CHANGES, HOUR_INTERVALS, HierarchyDataSource, HierarchyDataSourceBase, ImportStrategyDialogComponent, ImportStrategyDialogResult, ImportStrategyDialogService, ImportStrategyDto, InputDialogComponent, InputService, ListStateService, ListViewComponent, ListViewFiltersDirective, MINUTE_INTERVALS, MM_ACTION_ICONS, MM_ROW_ACTIONS_MAX_INLINE, MM_ROW_ACTIONS_MESSAGES, MessageDetailsDialogComponent, MessageDetailsDialogService, MessageListenerService, MmListViewDataBindingDirective, NOTIFICATION_DISPLAY_OPTIONS, NotificationDisplayService, OCTOBOT_ASSET_BASE_URL, OCTOBOT_FORCE_STILL, OCTOBOT_LOOP_MS, OCTOBOT_SIZE_PX, OctoBotComponent, PascalCasePipe, ProgressValue, ProgressWindowComponent, ProgressWindowService, RELATIVE_WEEKS, RowActionsComponent, SECOND_INTERVALS, SaveAsDialogComponent, SaveAsDialogService, TimeRangePickerComponent, TimeRangeUtils, TreeComponent, UnsavedChangesDirective, UnsavedChangesGuard, UploadFileDialogComponent, WEEKDAYS, WEEKDAY_ABBREVIATIONS, WindowStateService, actionAccessibleName, actionTooltip, applyDangerConfirmEnvironment, dangerConfirmNameMatches, dangerConfirmTypingToken, generateDayOfMonthOptions, generateHourOptions, generateMinuteOptions, isActionDisabled, isActionVisible, provideMmSharedUi, resolveListRowAction, resolveListRowLabel, resolveListViewMessages, resolveRowActionsMessages, sameAction, splitRowActions };
|
|
6573
7920
|
//# sourceMappingURL=meshmakers-shared-ui.mjs.map
|