@stemy/ngx-utils 19.9.47 → 19.9.49

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.
@@ -8455,23 +8455,43 @@ class CalendarComponent extends CalendarInputs {
8455
8455
  const val = this.validatedValue();
8456
8456
  untracked(() => {
8457
8457
  let referenceDate = null;
8458
- // 1. If a valid selection exists, use the latest date as the reference view anchor
8459
- if (Array.isArray(val) && val.length > 0) {
8460
- referenceDate = new Date(Math.max(...val.map(d => d.getTime())));
8461
- }
8462
- else if (val instanceof Date) {
8463
- referenceDate = val;
8458
+ const isMulti = this.isMultiSelect();
8459
+ if (isMulti) {
8460
+ // For multi-select, always initialize to the current month if not initialized yet.
8461
+ if (!this.isInitialized) {
8462
+ const today = new Date();
8463
+ const todayYear = today.getFullYear();
8464
+ const todayMonth = today.getMonth();
8465
+ // Check if today's month has at least one valid date, or if it is restricted.
8466
+ if (this.isMonthAvailable(todayYear, todayMonth)) {
8467
+ referenceDate = today;
8468
+ }
8469
+ else {
8470
+ const min = this.minDate();
8471
+ const max = this.maxDate();
8472
+ const disabledTimes = this.disabledTimestamps();
8473
+ const disabledDays = this.disabledDays();
8474
+ referenceDate = findClosestValidDate(today, min, max, disabledTimes, disabledDays);
8475
+ }
8476
+ }
8464
8477
  }
8465
- // 2. FALLBACK: If no selection exists, dynamically look up the first allowed calendar date
8466
- if (!referenceDate || isNaN(referenceDate.getTime())) {
8467
- const min = this.minDate();
8468
- const max = this.maxDate();
8469
- const disabledTimes = this.disabledTimestamps();
8470
- const disabledDays = this.disabledDays();
8471
- // Start searching from today
8472
- referenceDate = findClosestValidDate(new Date(), min, max, disabledTimes, disabledDays);
8478
+ else {
8479
+ // For single-select, align the view to the selected date or fall back to the closest valid date.
8480
+ if (val instanceof Date) {
8481
+ referenceDate = val;
8482
+ }
8483
+ else if (Array.isArray(val) && val.length > 0) {
8484
+ referenceDate = new Date(Math.max(...val.map(d => d.getTime())));
8485
+ }
8486
+ if (!referenceDate || isNaN(referenceDate.getTime())) {
8487
+ const min = this.minDate();
8488
+ const max = this.maxDate();
8489
+ const disabledTimes = this.disabledTimestamps();
8490
+ const disabledDays = this.disabledDays();
8491
+ referenceDate = findClosestValidDate(new Date(), min, max, disabledTimes, disabledDays);
8492
+ }
8473
8493
  }
8474
- // 3. Update the view tracking states cleanly
8494
+ // Update the view tracking states cleanly on initialization or when single-select value changes.
8475
8495
  if (referenceDate && !isNaN(referenceDate.getTime())) {
8476
8496
  this.currentMonth.set(referenceDate.getMonth());
8477
8497
  this.currentYear.set(referenceDate.getFullYear());
@@ -8624,6 +8644,13 @@ class CalendarComponent extends CalendarInputs {
8624
8644
  }
8625
8645
  this.value.set(Array.from(updatedSelectionMap.values()));
8626
8646
  }
8647
+ // Switch month/year if we chose a filler day
8648
+ const dragMonth = currentDrag.getMonth();
8649
+ const dragYear = currentDrag.getFullYear();
8650
+ if (dragMonth !== this.currentMonth() || dragYear !== this.currentYear()) {
8651
+ this.currentMonth.set(dragMonth);
8652
+ this.currentYear.set(dragYear);
8653
+ }
8627
8654
  }
8628
8655
  });
8629
8656
  this.isDragging.set(false);
@@ -8765,7 +8792,14 @@ class ChipsComponent {
8765
8792
  return true;
8766
8793
  }
8767
8794
  onBlur(ev) {
8768
- if (this.chipDropdown.isOpened)
8795
+ const relatedTarget = ev.relatedTarget;
8796
+ // If the focus is moving to an element inside the dropdown (like an option button),
8797
+ // we do not want to trigger the blur commit logic.
8798
+ const contentElement = this.chipDropdown?.contentElement;
8799
+ const insideDropdown = relatedTarget
8800
+ && contentElement instanceof HTMLElement
8801
+ && contentElement.contains(relatedTarget);
8802
+ if (insideDropdown)
8769
8803
  return;
8770
8804
  const input = ev.target;
8771
8805
  this.enterOption(input.value);
@@ -8855,13 +8889,13 @@ class ChipsComponent {
8855
8889
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.22", ngImport: i0, type: ChipsComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
8856
8890
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.22", type: ChipsComponent, isStandalone: false, selector: "chips", inputs: { testId: "testId", value: "value", multiple: "multiple", disabled: "disabled", type: "type", min: "min", max: "max", minLength: "minLength", maxLength: "maxLength", step: "step", placeholder: "placeholder", unique: "unique", strict: "strict", options: "options" }, outputs: { valueChange: "valueChange" }, providers: [
8857
8891
  { provide: NG_VALUE_ACCESSOR, useExisting: ChipsComponent, multi: true }
8858
- ], viewQueries: [{ propertyName: "chipDropdown", first: true, predicate: ["chipDropdown"], descendants: true }, { propertyName: "chipButtons", first: true, predicate: ["chipButtons"], descendants: true }, { propertyName: "chipInput", first: true, predicate: ["chipInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div dd\r\n attachTo=\"root\"\r\n [closeInside]=\"false\"\r\n [autoPlacement]=\"autoPlacement\"\r\n [ngClass]=\"{disabled: disabled}\" class=\"chips\" #chipDropdown=\"dropdown\">\r\n <input class=\"chips-input\"\r\n dropdownToggle\r\n [switch]=\"false\"\r\n [attr.data-testid]=\"testId + '-input'\"\r\n [type]=\"type == 'number' ? 'number': 'text'\"\r\n [min]=\"min\"\r\n [max]=\"max\"\r\n [step]=\"step\"\r\n [disabled]=\"disabled\"\r\n [placeholder]=\"valueOptions.length == 0 && placeholder | translate\"\r\n [ngStyle]=\"inputStyles\"\r\n (blur)=\"onBlur($event)\"\r\n (keyup)=\"onInput($event)\"\r\n #chipInput/>\r\n <div class=\"chips-buttons\" #chipButtons [ngClass]=\"{disabled: disabled}\" (resize)=\"onResize()\">\r\n <ng-container *ngFor=\"let item of valueOptions; let ix = index; trackBy:trackBy\">\r\n <a class=\"chips-button\" [ngClass]=\"'chips-' + statuses[ix]\" (dblclick)=\"removeItem($event, ix)\">\r\n @if (item.picture) {\r\n <img [src]=\"item.picture | safe:'url'\"\r\n class=\"chip-picture\"\r\n referrerpolicy=\"no-referrer\" [attr.alt]=\"item.label\">\r\n\r\n }\r\n <span class=\"chips-label\"\r\n [attr.data-testid]=\"testId + '-label-' + ix\">{{ item.label }} </span>\r\n <close-btn class=\"chips-remove\"\r\n [attr.data-testid]=\"testId + '-delete-' + ix\"\r\n (click)=\"removeItem($event, ix)\" *ngIf=\"!disabled\"></close-btn>\r\n </a>\r\n </ng-container>\r\n </div>\r\n @if (options) {\r\n <div class=\"chips-dropdown\" *dropdownContent>\r\n <button [ngClass]=\"option.classes\"\r\n [disabled]=\"option.disabled\"\r\n (click)=\"enterOption(option.value)\"\r\n *ngFor=\"let option of filteredOptions\">\r\n <div class=\"select-option\">\r\n @if (option?.picture) {\r\n <img [src]=\"option.picture | safe:'url'\"\r\n class=\"select-option-picture\"\r\n referrerpolicy=\"no-referrer\" [attr.alt]=\"option.label\">\r\n }\r\n <div class=\"select-option-label\">\r\n {{ option.label }}\r\n </div>\r\n </div>\r\n </button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [".chips{--chips-border-size: var(--border-size, 1px);--chips-border-radius: var(--border-radius, 5px);--chips-top-border-radius: var(--chips-border-radius) var(--chips-border-radius) 0 0;--chips-bottom-border-radius: 0 0 var(--chips-border-radius) var(--chips-border-radius);--chips-bg-color: var(--bg-color, #ffffff);--chips-border-color: var(--border-color, #ced4da);--chips-highlight-color: var(--highlight-color, var(--primary-color, #888888));--chips-highlight-text-color: var(--highlight-text-color, #ffffff);--chips-text-color: var(--text-color, #151515);--chips-text-size: var(--text-size, 16px);--chips-padding-vertical: 6px;--chips-padding-horizontal: 12px;--chips-padding: var(--chips-padding-vertical) var(--chips-padding-horizontal);--chips-btn-padding: 12px;--chips-btn-gap: calc(var(--chips-btn-padding) / 2);--chips-btn-color: white;--chips-btn-valid-color: rgba(200, 255, 200, .7);--chips-btn-invalid-color: rgba(255, 200, 200, .7);position:relative;margin:5px 0;font-size:var(--chips-text-size);padding:var(--chips-padding);background:var(--chips-bg-color);color:var(--chips-text-color);border:var(--chips-border-size) solid var(--chips-border-color);border-radius:var(--chips-border-radius)}.chips *{box-sizing:border-box}.chips .chips-input{background:var(--chips-bg-color);padding:var(--chips-padding);font-size:var(--chips-text-size);outline:none;border:none;width:100%;-webkit-user-select:none;user-select:none;font-weight:400}.chips .chips-buttons{position:absolute;top:var(--chips-padding-vertical);left:var(--chips-padding-horizontal);max-width:calc(100% - var(--chips-padding-horizontal) * 2);display:flex;gap:5px;overflow:auto;border-radius:var(--chips-border-radius)}.chips .chips-button{background:var(--chipd-btn-color);color:var(--chips-text-color);border:var(--chips-border-size) solid rgba(0,0,0,.2);border-radius:var(--chips-border-radius);padding:var(--chips-padding-vertical) var(--chips-btn-gap) var(--chips-padding-vertical) var(--chips-btn-padding);-webkit-user-select:none;user-select:none;font-weight:400;outline:none;display:flex;gap:var(--chips-btn-gap);justify-content:center;align-items:center;line-height:1.2rem;text-decoration:none}.chips .chips-button.chips-valid{--chipd-btn-color: var(--chips-btn-valid-color) }.chips .chips-button.chips-invalid{--chipd-btn-color: var(--chips-btn-invalid-color) }.chips img.chip-picture{width:28px;height:28px;object-fit:cover}.chips .chips-label{max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chips.disabled{opacity:.75}.chips.disabled .chips-button{padding-right:var(--chips-btn-padding)}.chips-dropdown:not(:empty){position:relative;z-index:1;width:var(--toggle-width, 0);display:flex;flex-direction:column;margin:-3px 0;padding:0;list-style:none;border:var(--chips-border-size) solid var(--chips-border-color);background:var(--chips-bg-color);border-radius:var(--chips-bottom-border-radius);overflow:hidden}.chips-dropdown:not(:empty) *{box-sizing:border-box}.chips-dropdown:not(:empty) button{border:none;background:none;cursor:pointer;padding:6px 12px;-webkit-user-select:none;user-select:none;text-align:left}.chips-dropdown:not(:empty) button:hover,.chips-dropdown:not(:empty) button.active{background-color:var(--chips-highlight-color);color:var(--chips-highlight-text-color)}.chips-dropdown:not(:empty) button:hover a,.chips-dropdown:not(:empty) button.active a{color:var(--chips-highlight-text-color)}.chips-dropdown:not(:empty) .select-option{display:flex;gap:8px;align-items:center;justify-content:center}.chips-dropdown:not(:empty) .select-option .select-option-picture{width:32px;height:32px;object-fit:cover}.chips-dropdown:not(:empty) .select-option .select-option-label{flex:1}.dropdown-placement-top .chips-dropdown{border-radius:var(--chips-top-border-radius)}\n"], dependencies: [{ kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { 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: "directive", type: i1$3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: DropdownDirective, selector: "[dd],[drop-down]", inputs: ["closeInside", "attachTo", "boundary", "placement", "autoPlacement", "mobileViewUnder", "fixed", "keyboardHandler", "isDisabled"], outputs: ["onShown", "onHidden", "onKeyboard"], exportAs: ["dropdown"] }, { kind: "directive", type: DropdownContentDirective, selector: "[dropdownContent]", exportAs: ["dropdown-content"] }, { kind: "directive", type: DropdownToggleDirective, selector: "[dropdownToggle]", inputs: ["beforeOpen", "switch"], exportAs: ["dropdown-toggle"] }, { kind: "component", type: CloseBtnComponent, selector: "close-btn" }, { kind: "pipe", type: SafeHtmlPipe, name: "safe" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
8892
+ ], viewQueries: [{ propertyName: "chipDropdown", first: true, predicate: ["chipDropdown"], descendants: true }, { propertyName: "chipButtons", first: true, predicate: ["chipButtons"], descendants: true }, { propertyName: "chipInput", first: true, predicate: ["chipInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div dd attachTo=\"root\" [closeInside]=\"false\" [autoPlacement]=\"autoPlacement\" [ngClass]=\"{disabled: disabled}\"\r\n class=\"chips\" #chipDropdown=\"dropdown\">\r\n <input class=\"chips-input\" dropdownToggle [switch]=\"false\" [attr.data-testid]=\"testId + '-input'\"\r\n [type]=\"type == 'number' ? 'number': 'text'\" [min]=\"min\" [max]=\"max\" [step]=\"step\" [disabled]=\"disabled\"\r\n [placeholder]=\"valueOptions.length == 0 && placeholder | translate\" [ngStyle]=\"inputStyles\"\r\n (blur)=\"onBlur($event)\" (keyup)=\"onInput($event)\" #chipInput />\r\n <div class=\"chips-buttons\" #chipButtons [ngClass]=\"{disabled: disabled}\" (resize)=\"onResize()\">\r\n <ng-container *ngFor=\"let item of valueOptions; let ix = index; trackBy:trackBy\">\r\n <a class=\"chips-button\" [ngClass]=\"'chips-' + statuses[ix]\" (dblclick)=\"removeItem($event, ix)\">\r\n @if (item.picture) {\r\n <img [src]=\"item.picture | safe:'url'\" class=\"chip-picture\" referrerpolicy=\"no-referrer\"\r\n [attr.alt]=\"item.label\">\r\n\r\n }\r\n <span class=\"chips-label\" [attr.data-testid]=\"testId + '-label-' + ix\">{{ item.label }} </span>\r\n <close-btn class=\"chips-remove\" [attr.data-testid]=\"testId + '-delete-' + ix\"\r\n (click)=\"removeItem($event, ix)\" *ngIf=\"!disabled\"></close-btn>\r\n </a>\r\n </ng-container>\r\n </div>\r\n @if (options) {\r\n <div class=\"chips-dropdown\" *dropdownContent>\r\n <button [ngClass]=\"option.classes\" [disabled]=\"option.disabled\" (click)=\"enterOption(option.value)\"\r\n *ngFor=\"let option of filteredOptions\">\r\n <div class=\"select-option\">\r\n @if (option?.picture) {\r\n <img [src]=\"option.picture | safe:'url'\" class=\"select-option-picture\" referrerpolicy=\"no-referrer\"\r\n [attr.alt]=\"option.label\">\r\n }\r\n <div class=\"select-option-label\">\r\n {{ option.label }}\r\n </div>\r\n </div>\r\n </button>\r\n </div>\r\n }\r\n</div>", styles: [".chips{--chips-border-size: var(--border-size, 1px);--chips-border-radius: var(--border-radius, 5px);--chips-top-border-radius: var(--chips-border-radius) var(--chips-border-radius) 0 0;--chips-bottom-border-radius: 0 0 var(--chips-border-radius) var(--chips-border-radius);--chips-bg-color: var(--bg-color, #ffffff);--chips-border-color: var(--border-color, #ced4da);--chips-highlight-color: var(--highlight-color, var(--primary-color, #888888));--chips-highlight-text-color: var(--highlight-text-color, #ffffff);--chips-text-color: var(--text-color, #151515);--chips-text-size: var(--text-size, 16px);--chips-padding-vertical: 6px;--chips-padding-horizontal: 12px;--chips-padding: var(--chips-padding-vertical) var(--chips-padding-horizontal);--chips-btn-padding: 12px;--chips-btn-gap: calc(var(--chips-btn-padding) / 2);--chips-btn-color: white;--chips-btn-valid-color: rgba(200, 255, 200, .7);--chips-btn-invalid-color: rgba(255, 200, 200, .7);position:relative;margin:5px 0;font-size:var(--chips-text-size);padding:var(--chips-padding);background:var(--chips-bg-color);color:var(--chips-text-color);border:var(--chips-border-size) solid var(--chips-border-color);border-radius:var(--chips-border-radius)}.chips *{box-sizing:border-box}.chips .chips-input{background:var(--chips-bg-color);padding:var(--chips-padding);font-size:var(--chips-text-size);outline:none;border:none;width:100%;-webkit-user-select:none;user-select:none;font-weight:400}.chips .chips-buttons{position:absolute;top:var(--chips-padding-vertical);left:var(--chips-padding-horizontal);max-width:calc(100% - var(--chips-padding-horizontal) * 2);display:flex;gap:5px;overflow:auto;border-radius:var(--chips-border-radius)}.chips .chips-button{background:var(--chipd-btn-color);color:var(--chips-text-color);border:var(--chips-border-size) solid rgba(0,0,0,.2);border-radius:var(--chips-border-radius);padding:var(--chips-padding-vertical) var(--chips-btn-gap) var(--chips-padding-vertical) var(--chips-btn-padding);-webkit-user-select:none;user-select:none;font-weight:400;outline:none;display:flex;gap:var(--chips-btn-gap);justify-content:center;align-items:center;line-height:1.2rem;text-decoration:none}.chips .chips-button.chips-valid{--chipd-btn-color: var(--chips-btn-valid-color) }.chips .chips-button.chips-invalid{--chipd-btn-color: var(--chips-btn-invalid-color) }.chips img.chip-picture{width:28px;height:28px;object-fit:cover}.chips .chips-label{max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chips.disabled{opacity:.75}.chips.disabled .chips-button{padding-right:var(--chips-btn-padding)}.chips-dropdown:not(:empty){position:relative;z-index:1;width:var(--toggle-width, 0);display:flex;flex-direction:column;margin:-3px 0;padding:0;list-style:none;border:var(--chips-border-size) solid var(--chips-border-color);background:var(--chips-bg-color);border-radius:var(--chips-bottom-border-radius);overflow:hidden}.chips-dropdown:not(:empty) *{box-sizing:border-box}.chips-dropdown:not(:empty) button{border:none;background:none;cursor:pointer;padding:6px 12px;-webkit-user-select:none;user-select:none;text-align:left}.chips-dropdown:not(:empty) button:hover,.chips-dropdown:not(:empty) button.active{background-color:var(--chips-highlight-color);color:var(--chips-highlight-text-color)}.chips-dropdown:not(:empty) button:hover a,.chips-dropdown:not(:empty) button.active a{color:var(--chips-highlight-text-color)}.chips-dropdown:not(:empty) .select-option{display:flex;gap:8px;align-items:center;justify-content:center}.chips-dropdown:not(:empty) .select-option .select-option-picture{width:32px;height:32px;object-fit:cover}.chips-dropdown:not(:empty) .select-option .select-option-label{flex:1}.dropdown-placement-top .chips-dropdown{border-radius:var(--chips-top-border-radius)}\n"], dependencies: [{ kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { 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: "directive", type: i1$3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: DropdownDirective, selector: "[dd],[drop-down]", inputs: ["closeInside", "attachTo", "boundary", "placement", "autoPlacement", "mobileViewUnder", "fixed", "keyboardHandler", "isDisabled"], outputs: ["onShown", "onHidden", "onKeyboard"], exportAs: ["dropdown"] }, { kind: "directive", type: DropdownContentDirective, selector: "[dropdownContent]", exportAs: ["dropdown-content"] }, { kind: "directive", type: DropdownToggleDirective, selector: "[dropdownToggle]", inputs: ["beforeOpen", "switch"], exportAs: ["dropdown-toggle"] }, { kind: "component", type: CloseBtnComponent, selector: "close-btn" }, { kind: "pipe", type: SafeHtmlPipe, name: "safe" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
8859
8893
  }
8860
8894
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.22", ngImport: i0, type: ChipsComponent, decorators: [{
8861
8895
  type: Component,
8862
8896
  args: [{ standalone: false, encapsulation: ViewEncapsulation.None, selector: "chips", providers: [
8863
8897
  { provide: NG_VALUE_ACCESSOR, useExisting: ChipsComponent, multi: true }
8864
- ], template: "<div dd\r\n attachTo=\"root\"\r\n [closeInside]=\"false\"\r\n [autoPlacement]=\"autoPlacement\"\r\n [ngClass]=\"{disabled: disabled}\" class=\"chips\" #chipDropdown=\"dropdown\">\r\n <input class=\"chips-input\"\r\n dropdownToggle\r\n [switch]=\"false\"\r\n [attr.data-testid]=\"testId + '-input'\"\r\n [type]=\"type == 'number' ? 'number': 'text'\"\r\n [min]=\"min\"\r\n [max]=\"max\"\r\n [step]=\"step\"\r\n [disabled]=\"disabled\"\r\n [placeholder]=\"valueOptions.length == 0 && placeholder | translate\"\r\n [ngStyle]=\"inputStyles\"\r\n (blur)=\"onBlur($event)\"\r\n (keyup)=\"onInput($event)\"\r\n #chipInput/>\r\n <div class=\"chips-buttons\" #chipButtons [ngClass]=\"{disabled: disabled}\" (resize)=\"onResize()\">\r\n <ng-container *ngFor=\"let item of valueOptions; let ix = index; trackBy:trackBy\">\r\n <a class=\"chips-button\" [ngClass]=\"'chips-' + statuses[ix]\" (dblclick)=\"removeItem($event, ix)\">\r\n @if (item.picture) {\r\n <img [src]=\"item.picture | safe:'url'\"\r\n class=\"chip-picture\"\r\n referrerpolicy=\"no-referrer\" [attr.alt]=\"item.label\">\r\n\r\n }\r\n <span class=\"chips-label\"\r\n [attr.data-testid]=\"testId + '-label-' + ix\">{{ item.label }} </span>\r\n <close-btn class=\"chips-remove\"\r\n [attr.data-testid]=\"testId + '-delete-' + ix\"\r\n (click)=\"removeItem($event, ix)\" *ngIf=\"!disabled\"></close-btn>\r\n </a>\r\n </ng-container>\r\n </div>\r\n @if (options) {\r\n <div class=\"chips-dropdown\" *dropdownContent>\r\n <button [ngClass]=\"option.classes\"\r\n [disabled]=\"option.disabled\"\r\n (click)=\"enterOption(option.value)\"\r\n *ngFor=\"let option of filteredOptions\">\r\n <div class=\"select-option\">\r\n @if (option?.picture) {\r\n <img [src]=\"option.picture | safe:'url'\"\r\n class=\"select-option-picture\"\r\n referrerpolicy=\"no-referrer\" [attr.alt]=\"option.label\">\r\n }\r\n <div class=\"select-option-label\">\r\n {{ option.label }}\r\n </div>\r\n </div>\r\n </button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [".chips{--chips-border-size: var(--border-size, 1px);--chips-border-radius: var(--border-radius, 5px);--chips-top-border-radius: var(--chips-border-radius) var(--chips-border-radius) 0 0;--chips-bottom-border-radius: 0 0 var(--chips-border-radius) var(--chips-border-radius);--chips-bg-color: var(--bg-color, #ffffff);--chips-border-color: var(--border-color, #ced4da);--chips-highlight-color: var(--highlight-color, var(--primary-color, #888888));--chips-highlight-text-color: var(--highlight-text-color, #ffffff);--chips-text-color: var(--text-color, #151515);--chips-text-size: var(--text-size, 16px);--chips-padding-vertical: 6px;--chips-padding-horizontal: 12px;--chips-padding: var(--chips-padding-vertical) var(--chips-padding-horizontal);--chips-btn-padding: 12px;--chips-btn-gap: calc(var(--chips-btn-padding) / 2);--chips-btn-color: white;--chips-btn-valid-color: rgba(200, 255, 200, .7);--chips-btn-invalid-color: rgba(255, 200, 200, .7);position:relative;margin:5px 0;font-size:var(--chips-text-size);padding:var(--chips-padding);background:var(--chips-bg-color);color:var(--chips-text-color);border:var(--chips-border-size) solid var(--chips-border-color);border-radius:var(--chips-border-radius)}.chips *{box-sizing:border-box}.chips .chips-input{background:var(--chips-bg-color);padding:var(--chips-padding);font-size:var(--chips-text-size);outline:none;border:none;width:100%;-webkit-user-select:none;user-select:none;font-weight:400}.chips .chips-buttons{position:absolute;top:var(--chips-padding-vertical);left:var(--chips-padding-horizontal);max-width:calc(100% - var(--chips-padding-horizontal) * 2);display:flex;gap:5px;overflow:auto;border-radius:var(--chips-border-radius)}.chips .chips-button{background:var(--chipd-btn-color);color:var(--chips-text-color);border:var(--chips-border-size) solid rgba(0,0,0,.2);border-radius:var(--chips-border-radius);padding:var(--chips-padding-vertical) var(--chips-btn-gap) var(--chips-padding-vertical) var(--chips-btn-padding);-webkit-user-select:none;user-select:none;font-weight:400;outline:none;display:flex;gap:var(--chips-btn-gap);justify-content:center;align-items:center;line-height:1.2rem;text-decoration:none}.chips .chips-button.chips-valid{--chipd-btn-color: var(--chips-btn-valid-color) }.chips .chips-button.chips-invalid{--chipd-btn-color: var(--chips-btn-invalid-color) }.chips img.chip-picture{width:28px;height:28px;object-fit:cover}.chips .chips-label{max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chips.disabled{opacity:.75}.chips.disabled .chips-button{padding-right:var(--chips-btn-padding)}.chips-dropdown:not(:empty){position:relative;z-index:1;width:var(--toggle-width, 0);display:flex;flex-direction:column;margin:-3px 0;padding:0;list-style:none;border:var(--chips-border-size) solid var(--chips-border-color);background:var(--chips-bg-color);border-radius:var(--chips-bottom-border-radius);overflow:hidden}.chips-dropdown:not(:empty) *{box-sizing:border-box}.chips-dropdown:not(:empty) button{border:none;background:none;cursor:pointer;padding:6px 12px;-webkit-user-select:none;user-select:none;text-align:left}.chips-dropdown:not(:empty) button:hover,.chips-dropdown:not(:empty) button.active{background-color:var(--chips-highlight-color);color:var(--chips-highlight-text-color)}.chips-dropdown:not(:empty) button:hover a,.chips-dropdown:not(:empty) button.active a{color:var(--chips-highlight-text-color)}.chips-dropdown:not(:empty) .select-option{display:flex;gap:8px;align-items:center;justify-content:center}.chips-dropdown:not(:empty) .select-option .select-option-picture{width:32px;height:32px;object-fit:cover}.chips-dropdown:not(:empty) .select-option .select-option-label{flex:1}.dropdown-placement-top .chips-dropdown{border-radius:var(--chips-top-border-radius)}\n"] }]
8898
+ ], template: "<div dd attachTo=\"root\" [closeInside]=\"false\" [autoPlacement]=\"autoPlacement\" [ngClass]=\"{disabled: disabled}\"\r\n class=\"chips\" #chipDropdown=\"dropdown\">\r\n <input class=\"chips-input\" dropdownToggle [switch]=\"false\" [attr.data-testid]=\"testId + '-input'\"\r\n [type]=\"type == 'number' ? 'number': 'text'\" [min]=\"min\" [max]=\"max\" [step]=\"step\" [disabled]=\"disabled\"\r\n [placeholder]=\"valueOptions.length == 0 && placeholder | translate\" [ngStyle]=\"inputStyles\"\r\n (blur)=\"onBlur($event)\" (keyup)=\"onInput($event)\" #chipInput />\r\n <div class=\"chips-buttons\" #chipButtons [ngClass]=\"{disabled: disabled}\" (resize)=\"onResize()\">\r\n <ng-container *ngFor=\"let item of valueOptions; let ix = index; trackBy:trackBy\">\r\n <a class=\"chips-button\" [ngClass]=\"'chips-' + statuses[ix]\" (dblclick)=\"removeItem($event, ix)\">\r\n @if (item.picture) {\r\n <img [src]=\"item.picture | safe:'url'\" class=\"chip-picture\" referrerpolicy=\"no-referrer\"\r\n [attr.alt]=\"item.label\">\r\n\r\n }\r\n <span class=\"chips-label\" [attr.data-testid]=\"testId + '-label-' + ix\">{{ item.label }} </span>\r\n <close-btn class=\"chips-remove\" [attr.data-testid]=\"testId + '-delete-' + ix\"\r\n (click)=\"removeItem($event, ix)\" *ngIf=\"!disabled\"></close-btn>\r\n </a>\r\n </ng-container>\r\n </div>\r\n @if (options) {\r\n <div class=\"chips-dropdown\" *dropdownContent>\r\n <button [ngClass]=\"option.classes\" [disabled]=\"option.disabled\" (click)=\"enterOption(option.value)\"\r\n *ngFor=\"let option of filteredOptions\">\r\n <div class=\"select-option\">\r\n @if (option?.picture) {\r\n <img [src]=\"option.picture | safe:'url'\" class=\"select-option-picture\" referrerpolicy=\"no-referrer\"\r\n [attr.alt]=\"option.label\">\r\n }\r\n <div class=\"select-option-label\">\r\n {{ option.label }}\r\n </div>\r\n </div>\r\n </button>\r\n </div>\r\n }\r\n</div>", styles: [".chips{--chips-border-size: var(--border-size, 1px);--chips-border-radius: var(--border-radius, 5px);--chips-top-border-radius: var(--chips-border-radius) var(--chips-border-radius) 0 0;--chips-bottom-border-radius: 0 0 var(--chips-border-radius) var(--chips-border-radius);--chips-bg-color: var(--bg-color, #ffffff);--chips-border-color: var(--border-color, #ced4da);--chips-highlight-color: var(--highlight-color, var(--primary-color, #888888));--chips-highlight-text-color: var(--highlight-text-color, #ffffff);--chips-text-color: var(--text-color, #151515);--chips-text-size: var(--text-size, 16px);--chips-padding-vertical: 6px;--chips-padding-horizontal: 12px;--chips-padding: var(--chips-padding-vertical) var(--chips-padding-horizontal);--chips-btn-padding: 12px;--chips-btn-gap: calc(var(--chips-btn-padding) / 2);--chips-btn-color: white;--chips-btn-valid-color: rgba(200, 255, 200, .7);--chips-btn-invalid-color: rgba(255, 200, 200, .7);position:relative;margin:5px 0;font-size:var(--chips-text-size);padding:var(--chips-padding);background:var(--chips-bg-color);color:var(--chips-text-color);border:var(--chips-border-size) solid var(--chips-border-color);border-radius:var(--chips-border-radius)}.chips *{box-sizing:border-box}.chips .chips-input{background:var(--chips-bg-color);padding:var(--chips-padding);font-size:var(--chips-text-size);outline:none;border:none;width:100%;-webkit-user-select:none;user-select:none;font-weight:400}.chips .chips-buttons{position:absolute;top:var(--chips-padding-vertical);left:var(--chips-padding-horizontal);max-width:calc(100% - var(--chips-padding-horizontal) * 2);display:flex;gap:5px;overflow:auto;border-radius:var(--chips-border-radius)}.chips .chips-button{background:var(--chipd-btn-color);color:var(--chips-text-color);border:var(--chips-border-size) solid rgba(0,0,0,.2);border-radius:var(--chips-border-radius);padding:var(--chips-padding-vertical) var(--chips-btn-gap) var(--chips-padding-vertical) var(--chips-btn-padding);-webkit-user-select:none;user-select:none;font-weight:400;outline:none;display:flex;gap:var(--chips-btn-gap);justify-content:center;align-items:center;line-height:1.2rem;text-decoration:none}.chips .chips-button.chips-valid{--chipd-btn-color: var(--chips-btn-valid-color) }.chips .chips-button.chips-invalid{--chipd-btn-color: var(--chips-btn-invalid-color) }.chips img.chip-picture{width:28px;height:28px;object-fit:cover}.chips .chips-label{max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chips.disabled{opacity:.75}.chips.disabled .chips-button{padding-right:var(--chips-btn-padding)}.chips-dropdown:not(:empty){position:relative;z-index:1;width:var(--toggle-width, 0);display:flex;flex-direction:column;margin:-3px 0;padding:0;list-style:none;border:var(--chips-border-size) solid var(--chips-border-color);background:var(--chips-bg-color);border-radius:var(--chips-bottom-border-radius);overflow:hidden}.chips-dropdown:not(:empty) *{box-sizing:border-box}.chips-dropdown:not(:empty) button{border:none;background:none;cursor:pointer;padding:6px 12px;-webkit-user-select:none;user-select:none;text-align:left}.chips-dropdown:not(:empty) button:hover,.chips-dropdown:not(:empty) button.active{background-color:var(--chips-highlight-color);color:var(--chips-highlight-text-color)}.chips-dropdown:not(:empty) button:hover a,.chips-dropdown:not(:empty) button.active a{color:var(--chips-highlight-text-color)}.chips-dropdown:not(:empty) .select-option{display:flex;gap:8px;align-items:center;justify-content:center}.chips-dropdown:not(:empty) .select-option .select-option-picture{width:32px;height:32px;object-fit:cover}.chips-dropdown:not(:empty) .select-option .select-option-label{flex:1}.dropdown-placement-top .chips-dropdown{border-radius:var(--chips-top-border-radius)}\n"] }]
8865
8899
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { testId: [{
8866
8900
  type: Input
8867
8901
  }], value: [{
@@ -8911,30 +8945,73 @@ class CodeEditorComponent {
8911
8945
  get rootNode() {
8912
8946
  return this.root;
8913
8947
  }
8914
- constructor(cdr, element) {
8915
- this.cdr = cdr;
8916
- this.element = element;
8917
- this.value = "";
8918
- this.lang = "json";
8919
- this.onChange = () => {
8920
- };
8921
- this.onTouched = () => {
8922
- };
8948
+ constructor() {
8949
+ this.value = model("");
8950
+ this.lang = input("json");
8951
+ this.disabled = model(false);
8952
+ this.onChange = () => { };
8953
+ this.onTouched = () => { };
8954
+ this.cdr = inject(ChangeDetectorRef);
8955
+ this.element = inject((ElementRef));
8923
8956
  this.extensions = {};
8957
+ this.editor = signal(null);
8958
+ this.editorElem = viewChild("editor");
8959
+ effect(() => {
8960
+ const editor = this.editor();
8961
+ if (!editor)
8962
+ return;
8963
+ const value = this.value() || "";
8964
+ const disabled = this.disabled();
8965
+ const expectedStr = !isString(value) ? JSON.stringify(value, null, 4) : value;
8966
+ const currentStr = editor.state.doc.toString();
8967
+ const langExtension = this.getLangExtension();
8968
+ const { EditorView } = CM["@codemirror/view"];
8969
+ const dispatchData = {
8970
+ effects: [
8971
+ this.langCompartment.reconfigure(langExtension),
8972
+ this.editableCompartment.reconfigure(EditorView.editable.of(!disabled))
8973
+ ]
8974
+ };
8975
+ if (currentStr !== expectedStr) {
8976
+ dispatchData.changes = {
8977
+ from: 0,
8978
+ to: editor.state.doc.length,
8979
+ insert: expectedStr
8980
+ };
8981
+ }
8982
+ editor.dispatch(dispatchData);
8983
+ });
8924
8984
  }
8925
8985
  getLangExtension() {
8926
- if (!this.extensions[this.lang]) {
8927
- const lang = CM[`@codemirror/lang-${this.lang || "json"}`];
8928
- const ext = lang[this.lang];
8929
- this.extensions[this.lang] = ext();
8986
+ const langVal = this.lang();
8987
+ if (!this.extensions[langVal]) {
8988
+ const lang = CM[`@codemirror/lang-${langVal || "json"}`];
8989
+ const ext = lang[langVal];
8990
+ this.extensions[langVal] = ext();
8930
8991
  }
8931
- return this.extensions[this.lang];
8992
+ return this.extensions[langVal];
8932
8993
  }
8933
8994
  ngAfterViewInit() {
8934
8995
  Promise.all([
8935
8996
  LoaderUtils.loadScript("https://codemirror.net/codemirror.js", true),
8936
8997
  LoaderUtils.loadScript("https://cdn.jsdelivr.net/npm/jsonlint", true),
8937
- ]).then(() => {
8998
+ ]).then(() => this.initEditor());
8999
+ }
9000
+ registerOnChange(fn) {
9001
+ this.onChange = fn;
9002
+ }
9003
+ registerOnTouched(fn) {
9004
+ this.onTouched = fn;
9005
+ }
9006
+ writeValue(value) {
9007
+ this.value.set(value);
9008
+ this.cdr.markForCheck();
9009
+ }
9010
+ setDisabledState(isDisabled) {
9011
+ this.disabled.set(isDisabled);
9012
+ }
9013
+ initEditor() {
9014
+ untracked(() => {
8938
9015
  const { basicSetup } = CM["codemirror"];
8939
9016
  const { Compartment } = CM["@codemirror/state"];
8940
9017
  const { EditorView } = CM["@codemirror/view"];
@@ -8943,7 +9020,7 @@ class CodeEditorComponent {
8943
9020
  const jsonLinter = linter((view) => {
8944
9021
  const diagnostics = [];
8945
9022
  const value = view.state.doc.toString();
8946
- if (!value.trim() || this.lang !== "json")
9023
+ if (!value.trim() || this.lang() !== "json")
8947
9024
  return diagnostics;
8948
9025
  try {
8949
9026
  jsonlint.parse(value);
@@ -8966,83 +9043,54 @@ class CodeEditorComponent {
8966
9043
  if (update.docChanged) {
8967
9044
  // Grab the full string payload of the document
8968
9045
  const value = update.state.doc.toString();
8969
- if (this.lang === "json") {
9046
+ const lang = untracked(() => this.lang());
9047
+ let parsedValue;
9048
+ if (lang === "json") {
8970
9049
  try {
8971
- this.value = JSON.parse(value);
9050
+ parsedValue = JSON.parse(value);
8972
9051
  }
8973
9052
  catch (e) {
8974
9053
  return null;
8975
9054
  }
8976
9055
  }
8977
9056
  else {
8978
- this.value = value;
9057
+ parsedValue = value;
8979
9058
  }
8980
- this.onChange(this.value);
8981
- this.onTouched(this.value);
9059
+ this.value.set(parsedValue);
9060
+ this.onChange(parsedValue);
9061
+ this.onTouched(parsedValue);
8982
9062
  }
8983
9063
  });
8984
9064
  // Initialize editor on an HTMLElement
8985
- const value = this.value || "";
9065
+ const value = this.value() || "";
9066
+ const disabled = this.disabled();
8986
9067
  this.langCompartment = new Compartment();
8987
- this.editor = new EditorView({
9068
+ this.editableCompartment = new Compartment();
9069
+ this.editor.set(new EditorView({
8988
9070
  doc: !isString(value) ? JSON.stringify(value, null, 4) : value,
8989
9071
  extensions: [
8990
9072
  basicSetup,
8991
9073
  this.langCompartment.of(langExtension),
9074
+ this.editableCompartment.of(EditorView.editable.of(!disabled)),
9075
+ EditorView.lineWrapping,
8992
9076
  jsonLinter,
8993
9077
  changeHandler
8994
9078
  ],
8995
- parent: this.editorElem.nativeElement
8996
- });
8997
- });
8998
- }
8999
- ngOnChanges() {
9000
- if (!this.editor)
9001
- return;
9002
- const value = this.value || "";
9003
- this.lang = EDITOR_TYPES.includes(this.lang) ? this.lang : "json";
9004
- this.editor.dispatch({
9005
- effects: this.langCompartment.reconfigure(this.getLangExtension()),
9006
- changes: {
9007
- from: 0,
9008
- to: this.editor.state.doc.length,
9009
- insert: !isString(value) ? JSON.stringify(value, null, 4) : value
9010
- }
9079
+ parent: this.editorElem()?.nativeElement
9080
+ }));
9011
9081
  });
9012
9082
  }
9013
- registerOnChange(fn) {
9014
- this.onChange = fn;
9015
- }
9016
- registerOnTouched(fn) {
9017
- this.onTouched = fn;
9018
- }
9019
- writeValue(value) {
9020
- this.value = value;
9021
- this.cdr.markForCheck();
9022
- this.ngOnChanges();
9023
- }
9024
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.22", ngImport: i0, type: CodeEditorComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
9025
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.22", type: CodeEditorComponent, isStandalone: false, selector: "code-editor", inputs: { value: "value", lang: "lang", disabled: "disabled" }, outputs: { valueChange: "valueChange" }, providers: [
9083
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.22", ngImport: i0, type: CodeEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
9084
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.2.22", type: CodeEditorComponent, isStandalone: false, selector: "code-editor", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, lang: { classPropertyName: "lang", publicName: "lang", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", disabled: "disabledChange" }, providers: [
9026
9085
  { provide: NG_VALUE_ACCESSOR, useExisting: CodeEditorComponent, multi: true }
9027
- ], viewQueries: [{ propertyName: "editorElem", first: true, predicate: ["editor"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"code-editor\" [ngClass]=\"{disabled: disabled}\">\r\n <div #editor></div>\r\n</div>\r\n", styles: [".code-editor>div{margin:10px 0}.code-editor>div .cm-editor{outline:none}.code-editor>div .cm-scroller{min-height:150px;max-height:345px;background:#fefefe;border:1px solid #ddd}\n"], dependencies: [{ kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], encapsulation: i0.ViewEncapsulation.None }); }
9086
+ ], viewQueries: [{ propertyName: "editorElem", first: true, predicate: ["editor"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"code-editor\" [ngClass]=\"{disabled: disabled()}\">\r\n <div #editor></div>\r\n</div>\r\n", styles: [".code-editor>div{margin:10px 0}.code-editor>div .cm-editor{outline:none}.code-editor>div .cm-scroller{min-height:150px;max-height:345px;background:#fefefe;border:1px solid #ddd}.code-editor.disabled .cm-content{pointer-events:none;-webkit-user-select:none;user-select:none}.code-editor.disabled .cm-content .cm-activeLine{background-color:transparent}\n"], dependencies: [{ kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], encapsulation: i0.ViewEncapsulation.None }); }
9028
9087
  }
9029
9088
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.22", ngImport: i0, type: CodeEditorComponent, decorators: [{
9030
9089
  type: Component,
9031
9090
  args: [{ standalone: false, selector: "code-editor", encapsulation: ViewEncapsulation.None, providers: [
9032
9091
  { provide: NG_VALUE_ACCESSOR, useExisting: CodeEditorComponent, multi: true }
9033
- ], template: "<div class=\"code-editor\" [ngClass]=\"{disabled: disabled}\">\r\n <div #editor></div>\r\n</div>\r\n", styles: [".code-editor>div{margin:10px 0}.code-editor>div .cm-editor{outline:none}.code-editor>div .cm-scroller{min-height:150px;max-height:345px;background:#fefefe;border:1px solid #ddd}\n"] }]
9034
- }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }], propDecorators: { value: [{
9035
- type: Input
9036
- }], lang: [{
9037
- type: Input
9038
- }], disabled: [{
9039
- type: Input
9040
- }], valueChange: [{
9041
- type: Output
9042
- }], editorElem: [{
9043
- type: ViewChild,
9044
- args: ["editor"]
9045
- }] } });
9092
+ ], template: "<div class=\"code-editor\" [ngClass]=\"{disabled: disabled()}\">\r\n <div #editor></div>\r\n</div>\r\n", styles: [".code-editor>div{margin:10px 0}.code-editor>div .cm-editor{outline:none}.code-editor>div .cm-scroller{min-height:150px;max-height:345px;background:#fefefe;border:1px solid #ddd}.code-editor.disabled .cm-content{pointer-events:none;-webkit-user-select:none;user-select:none}.code-editor.disabled .cm-content .cm-activeLine{background-color:transparent}\n"] }]
9093
+ }], ctorParameters: () => [] });
9046
9094
 
9047
9095
  class DatePickerComponent extends CalendarInputs {
9048
9096
  constructor() {