@memberjunction/ng-ui-components 6.1.4 → 6.2.0-edge.1

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.
Files changed (81) hide show
  1. package/README.md +35 -9
  2. package/dist/lib/a11y/named-control.base.d.ts +91 -0
  3. package/dist/lib/a11y/named-control.base.d.ts.map +1 -0
  4. package/dist/lib/a11y/named-control.base.js +121 -0
  5. package/dist/lib/a11y/named-control.base.js.map +1 -0
  6. package/dist/lib/a11y/unnamed-control-guard.d.ts +52 -0
  7. package/dist/lib/a11y/unnamed-control-guard.d.ts.map +1 -0
  8. package/dist/lib/a11y/unnamed-control-guard.js +94 -0
  9. package/dist/lib/a11y/unnamed-control-guard.js.map +1 -0
  10. package/dist/lib/accordion/accordion.component.d.ts +16 -4
  11. package/dist/lib/accordion/accordion.component.d.ts.map +1 -1
  12. package/dist/lib/accordion/accordion.component.js +36 -12
  13. package/dist/lib/accordion/accordion.component.js.map +1 -1
  14. package/dist/lib/applied-filters/applied-filters.component.js.map +1 -1
  15. package/dist/lib/button/button.directive.d.ts +34 -6
  16. package/dist/lib/button/button.directive.d.ts.map +1 -1
  17. package/dist/lib/button/button.directive.js +85 -25
  18. package/dist/lib/button/button.directive.js.map +1 -1
  19. package/dist/lib/card-grid/card-grid.component.d.ts +3 -1
  20. package/dist/lib/card-grid/card-grid.component.d.ts.map +1 -1
  21. package/dist/lib/card-grid/card-grid.component.js +13 -9
  22. package/dist/lib/card-grid/card-grid.component.js.map +1 -1
  23. package/dist/lib/clickable/clickable.directive.d.ts +11 -3
  24. package/dist/lib/clickable/clickable.directive.d.ts.map +1 -1
  25. package/dist/lib/clickable/clickable.directive.js +25 -5
  26. package/dist/lib/clickable/clickable.directive.js.map +1 -1
  27. package/dist/lib/combobox/combobox.component.d.ts +44 -3
  28. package/dist/lib/combobox/combobox.component.d.ts.map +1 -1
  29. package/dist/lib/combobox/combobox.component.js +135 -51
  30. package/dist/lib/combobox/combobox.component.js.map +1 -1
  31. package/dist/lib/confirm-dialog/confirm-dialog.component.d.ts +6 -0
  32. package/dist/lib/confirm-dialog/confirm-dialog.component.d.ts.map +1 -1
  33. package/dist/lib/confirm-dialog/confirm-dialog.component.js +16 -4
  34. package/dist/lib/confirm-dialog/confirm-dialog.component.js.map +1 -1
  35. package/dist/lib/datepicker/datepicker.component.d.ts +25 -2
  36. package/dist/lib/datepicker/datepicker.component.d.ts.map +1 -1
  37. package/dist/lib/datepicker/datepicker.component.js +98 -42
  38. package/dist/lib/datepicker/datepicker.component.js.map +1 -1
  39. package/dist/lib/dialog/dialog.component.d.ts +4 -0
  40. package/dist/lib/dialog/dialog.component.d.ts.map +1 -1
  41. package/dist/lib/dialog/dialog.component.js +12 -4
  42. package/dist/lib/dialog/dialog.component.js.map +1 -1
  43. package/dist/lib/dialog/dialog.service.js.map +1 -1
  44. package/dist/lib/dropdown/dropdown.component.d.ts +19 -44
  45. package/dist/lib/dropdown/dropdown.component.d.ts.map +1 -1
  46. package/dist/lib/dropdown/dropdown.component.js +37 -76
  47. package/dist/lib/dropdown/dropdown.component.js.map +1 -1
  48. package/dist/lib/dropdown/dropdown.scss +0 -16
  49. package/dist/lib/filter-panel/filter-panel.component.d.ts +8 -0
  50. package/dist/lib/filter-panel/filter-panel.component.d.ts.map +1 -1
  51. package/dist/lib/filter-panel/filter-panel.component.js +25 -9
  52. package/dist/lib/filter-panel/filter-panel.component.js.map +1 -1
  53. package/dist/lib/numeric-input/numeric-input.component.d.ts +15 -1
  54. package/dist/lib/numeric-input/numeric-input.component.d.ts.map +1 -1
  55. package/dist/lib/numeric-input/numeric-input.component.js +36 -9
  56. package/dist/lib/numeric-input/numeric-input.component.js.map +1 -1
  57. package/dist/lib/page-search/page-search.component.d.ts +32 -3
  58. package/dist/lib/page-search/page-search.component.d.ts.map +1 -1
  59. package/dist/lib/page-search/page-search.component.js +68 -12
  60. package/dist/lib/page-search/page-search.component.js.map +1 -1
  61. package/dist/lib/switch/switch.component.d.ts +22 -1
  62. package/dist/lib/switch/switch.component.d.ts.map +1 -1
  63. package/dist/lib/switch/switch.component.js +50 -16
  64. package/dist/lib/switch/switch.component.js.map +1 -1
  65. package/dist/lib/tabs/tab-chrome-guard.d.ts +2 -0
  66. package/dist/lib/tabs/tab-chrome-guard.d.ts.map +1 -1
  67. package/dist/lib/tabs/tab-chrome-guard.js +5 -1
  68. package/dist/lib/tabs/tab-chrome-guard.js.map +1 -1
  69. package/dist/lib/window/window.component.d.ts +10 -0
  70. package/dist/lib/window/window.component.d.ts.map +1 -1
  71. package/dist/lib/window/window.component.js +27 -7
  72. package/dist/lib/window/window.component.js.map +1 -1
  73. package/dist/lib/workspace-tabs/workspace-tab-strip.component.d.ts +10 -0
  74. package/dist/lib/workspace-tabs/workspace-tab-strip.component.d.ts.map +1 -1
  75. package/dist/lib/workspace-tabs/workspace-tab-strip.component.js +27 -7
  76. package/dist/lib/workspace-tabs/workspace-tab-strip.component.js.map +1 -1
  77. package/dist/public-api.d.ts +2 -0
  78. package/dist/public-api.d.ts.map +1 -1
  79. package/dist/public-api.js +2 -0
  80. package/dist/public-api.js.map +1 -1
  81. package/package.json +2 -2
@@ -2,63 +2,69 @@ import { Component, Input, Output, EventEmitter, forwardRef, HostBinding, ViewCh
2
2
  import { NgTemplateOutlet } from '@angular/common';
3
3
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
4
4
  import { OverlayModule } from '@angular/cdk/overlay';
5
+ import { MJNamedControlBase } from '../a11y/named-control.base';
6
+ import { WarnIfUnnamed } from '../a11y/unnamed-control-guard';
5
7
  import * as i0 from "@angular/core";
6
8
  import * as i1 from "@angular/cdk/overlay";
7
9
  const _c0 = ["mjComboboxItem"];
8
10
  const _c1 = ["trigger"];
9
11
  const _c2 = ["comboInput"];
10
12
  const _c3 = a0 => ({ $implicit: a0 });
11
- function MJComboboxComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
13
+ function MJComboboxComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
12
14
  const _r1 = i0.ɵɵgetCurrentView();
13
- i0.ɵɵelementStart(0, "button", 8);
14
- i0.ɵɵlistener("mousedown", function MJComboboxComponent_Conditional_5_Template_button_mousedown_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnClear($event)); });
15
- i0.ɵɵelement(1, "i", 9);
15
+ i0.ɵɵelementStart(0, "button", 9);
16
+ i0.ɵɵlistener("mousedown", function MJComboboxComponent_Conditional_9_Template_button_mousedown_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnClear($event)); });
17
+ i0.ɵɵelement(1, "i", 10);
16
18
  i0.ɵɵelementEnd();
19
+ } if (rf & 2) {
20
+ const ctx_r1 = i0.ɵɵnextContext();
21
+ i0.ɵɵattribute("aria-labelledby", ctx_r1.ClearLabelledBy || null)("aria-label", ctx_r1.ClearLabelledBy ? null : ctx_r1.ClearLabel);
17
22
  } }
18
- function MJComboboxComponent_ng_template_8_For_2_Conditional_1_ng_container_0_Template(rf, ctx) { if (rf & 1) {
23
+ function MJComboboxComponent_ng_template_12_For_2_Conditional_1_ng_container_0_Template(rf, ctx) { if (rf & 1) {
19
24
  i0.ɵɵelementContainer(0);
20
25
  } }
21
- function MJComboboxComponent_ng_template_8_For_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
22
- i0.ɵɵtemplate(0, MJComboboxComponent_ng_template_8_For_2_Conditional_1_ng_container_0_Template, 1, 0, "ng-container", 14);
26
+ function MJComboboxComponent_ng_template_12_For_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
27
+ i0.ɵɵtemplate(0, MJComboboxComponent_ng_template_12_For_2_Conditional_1_ng_container_0_Template, 1, 0, "ng-container", 15);
23
28
  } if (rf & 2) {
24
29
  const item_r4 = i0.ɵɵnextContext().$implicit;
25
30
  const ctx_r1 = i0.ɵɵnextContext(2);
26
31
  i0.ɵɵproperty("ngTemplateOutlet", ctx_r1.ItemTemplate)("ngTemplateOutletContext", i0.ɵɵpureFunction1(2, _c3, item_r4));
27
32
  } }
28
- function MJComboboxComponent_ng_template_8_For_2_Conditional_2_Template(rf, ctx) { if (rf & 1) {
33
+ function MJComboboxComponent_ng_template_12_For_2_Conditional_2_Template(rf, ctx) { if (rf & 1) {
29
34
  i0.ɵɵtext(0);
30
35
  } if (rf & 2) {
31
36
  const item_r4 = i0.ɵɵnextContext().$implicit;
32
37
  const ctx_r1 = i0.ɵɵnextContext(2);
33
38
  i0.ɵɵtextInterpolate1(" ", ctx_r1.GetItemText(item_r4), " ");
34
39
  } }
35
- function MJComboboxComponent_ng_template_8_For_2_Template(rf, ctx) { if (rf & 1) {
40
+ function MJComboboxComponent_ng_template_12_For_2_Template(rf, ctx) { if (rf & 1) {
36
41
  const _r3 = i0.ɵɵgetCurrentView();
37
- i0.ɵɵelementStart(0, "div", 13);
38
- i0.ɵɵlistener("mousedown", function MJComboboxComponent_ng_template_8_For_2_Template_div_mousedown_0_listener($event) { const item_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SelectItem(item_r4, $event)); });
39
- i0.ɵɵconditionalCreate(1, MJComboboxComponent_ng_template_8_For_2_Conditional_1_Template, 1, 4, "ng-container")(2, MJComboboxComponent_ng_template_8_For_2_Conditional_2_Template, 1, 1);
42
+ i0.ɵɵelementStart(0, "div", 14);
43
+ i0.ɵɵlistener("mousedown", function MJComboboxComponent_ng_template_12_For_2_Template_div_mousedown_0_listener($event) { const item_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SelectItem(item_r4, $event)); });
44
+ i0.ɵɵconditionalCreate(1, MJComboboxComponent_ng_template_12_For_2_Conditional_1_Template, 1, 4, "ng-container")(2, MJComboboxComponent_ng_template_12_For_2_Conditional_2_Template, 1, 1);
40
45
  i0.ɵɵelementEnd();
41
46
  } if (rf & 2) {
42
47
  const item_r4 = ctx.$implicit;
43
48
  const $index_r5 = ctx.$index;
44
49
  const ctx_r1 = i0.ɵɵnextContext(2);
45
50
  i0.ɵɵclassProp("mj-dropdown-option--selected", ctx_r1.IsItemSelected(item_r4))("mj-dropdown-option--highlighted", ctx_r1.HighlightedIndex === $index_r5);
46
- i0.ɵɵattribute("aria-selected", ctx_r1.IsItemSelected(item_r4));
51
+ i0.ɵɵattribute("id", ctx_r1.OptionId($index_r5))("aria-selected", ctx_r1.IsItemSelected(item_r4));
47
52
  i0.ɵɵadvance();
48
53
  i0.ɵɵconditional(ctx_r1.ItemTemplate ? 1 : 2);
49
54
  } }
50
- function MJComboboxComponent_ng_template_8_Conditional_3_Template(rf, ctx) { if (rf & 1) {
51
- i0.ɵɵelementStart(0, "div", 12);
55
+ function MJComboboxComponent_ng_template_12_Conditional_3_Template(rf, ctx) { if (rf & 1) {
56
+ i0.ɵɵelementStart(0, "div", 13);
52
57
  i0.ɵɵtext(1, "No data found");
53
58
  i0.ɵɵelementEnd();
54
59
  } }
55
- function MJComboboxComponent_ng_template_8_Template(rf, ctx) { if (rf & 1) {
56
- i0.ɵɵelementStart(0, "div", 10);
57
- i0.ɵɵrepeaterCreate(1, MJComboboxComponent_ng_template_8_For_2_Template, 3, 6, "div", 11, i0.ɵɵcomponentInstance().TrackByIndex, true);
58
- i0.ɵɵconditionalCreate(3, MJComboboxComponent_ng_template_8_Conditional_3_Template, 2, 0, "div", 12);
60
+ function MJComboboxComponent_ng_template_12_Template(rf, ctx) { if (rf & 1) {
61
+ i0.ɵɵelementStart(0, "div", 11);
62
+ i0.ɵɵrepeaterCreate(1, MJComboboxComponent_ng_template_12_For_2_Template, 3, 7, "div", 12, i0.ɵɵcomponentInstance().TrackByIndex, true);
63
+ i0.ɵɵconditionalCreate(3, MJComboboxComponent_ng_template_12_Conditional_3_Template, 2, 0, "div", 13);
59
64
  i0.ɵɵelementEnd();
60
65
  } if (rf & 2) {
61
66
  const ctx_r1 = i0.ɵɵnextContext();
67
+ i0.ɵɵattribute("id", ctx_r1.ListboxId)("aria-label", ctx_r1.AriaLabel || null)("aria-labelledby", ctx_r1.AriaLabelledBy || null);
62
68
  i0.ɵɵadvance();
63
69
  i0.ɵɵrepeater(ctx_r1.FilteredItems);
64
70
  i0.ɵɵadvance(2);
@@ -71,6 +77,20 @@ function MJComboboxComponent_ng_template_8_Template(rf, ctx) { if (rf & 1) {
71
77
  * that filters the list as you type. When AllowCustom is true, typed text that
72
78
  * doesn't match any item is emitted as the value.
73
79
  *
80
+ * Every combobox needs an ACCESSIBLE NAME, or it announces as "combobox, collapsed" with no hint of
81
+ * what it selects (WCAG 2.1 4.1.2). Use {@link MJNamedControlBase.AriaLabelledBy} when a visible
82
+ * label already exists and {@link MJNamedControlBase.AriaLabel} when none does. Here the trigger is
83
+ * a real `<input>`, so {@link MJNamedControlBase.InputId} IS a valid `<label for>` target — unlike
84
+ * `mj-dropdown`, whose trigger is a div.
85
+ *
86
+ * @example With a visible label (preferred)
87
+ * ```html
88
+ * <span id="category-label">Category</span>
89
+ * <mj-combobox AriaLabelledBy="category-label" [Data]="categories" TextField="text"
90
+ * ValueField="value" [(ngModel)]="selectedCategory" [ValuePrimitive]="true">
91
+ * </mj-combobox>
92
+ * ```
93
+ *
74
94
  * @example
75
95
  * ```html
76
96
  * <mj-combobox
@@ -85,7 +105,7 @@ function MJComboboxComponent_ng_template_8_Template(rf, ctx) { if (rf & 1) {
85
105
  * </mj-combobox>
86
106
  * ```
87
107
  */
88
- export class MJComboboxComponent {
108
+ export class MJComboboxComponent extends MJNamedControlBase {
89
109
  set Data(value) {
90
110
  this._data = value;
91
111
  // Re-resolve display text when data arrives after writeValue
@@ -135,6 +155,34 @@ export class MJComboboxComponent {
135
155
  { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top' },
136
156
  { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom' }
137
157
  ];
158
+ /**
159
+ * Id of the popup listbox, so the input can point `aria-controls` at it while open — the half of
160
+ * the combobox pattern that makes "collapsed/expanded" refer to something a screen reader can
161
+ * find.
162
+ */
163
+ get ListboxId() { return `mj-combobox-listbox-${this.NamedControlId}`; }
164
+ /** Id of one rendered option, referenced by {@link ActiveDescendantId}. */
165
+ OptionId(index) { return `mj-combobox-option-${this.NamedControlId}-${index}`; }
166
+ /**
167
+ * `aria-activedescendant` for the input: the option the arrow keys have moved to. Focus stays in
168
+ * the input the whole time, so without this the highlight is a CSS class and nothing else — a
169
+ * screen-reader user arrowing through the list hears no change at all.
170
+ */
171
+ get ActiveDescendantId() {
172
+ const active = this.IsOpen && this.HighlightedIndex >= 0 && this.HighlightedIndex < this.FilteredItems.length;
173
+ return active ? this.OptionId(this.HighlightedIndex) : null;
174
+ }
175
+ /** `aria-labelledby` id list for the clear button when a VISIBLE label names the combobox. */
176
+ get ClearLabelledBy() { return this.SecondaryLabelledBy('clear-word'); }
177
+ /** `aria-label` for the clear button in the no-visible-label case. */
178
+ get ClearLabel() { return this.SecondaryLabel('Clear', 'Clear'); }
179
+ /** `aria-labelledby` id list for the toggle button when a VISIBLE label names the combobox. */
180
+ get ToggleLabelledBy() { return this.SecondaryLabelledBy('toggle-word'); }
181
+ /**
182
+ * `aria-label` for the toggle button in the no-visible-label case. It had no name at all before
183
+ * #4116 — an unnamed button next to a named input, which announces as "button".
184
+ */
185
+ get ToggleLabel() { return this.SecondaryLabel('Show options for', 'Show options'); }
138
186
  onChange = () => { };
139
187
  onTouched = () => { };
140
188
  isBlurring = false;
@@ -178,7 +226,7 @@ export class MJComboboxComponent {
178
226
  if (!this.isBlurring)
179
227
  return;
180
228
  this.isBlurring = false;
181
- this.CommitValue();
229
+ this.commitValue();
182
230
  this.Close();
183
231
  this.onTouched();
184
232
  }, 150);
@@ -246,7 +294,7 @@ export class MJComboboxComponent {
246
294
  this.cdr.detectChanges();
247
295
  }
248
296
  /** Commit the current input text as a value (for AllowCustom or matching item) */
249
- CommitValue() {
297
+ commitValue() {
250
298
  if (!this.InputText.trim()) {
251
299
  // Empty input — clear the value
252
300
  if (this.SelectedValue != null) {
@@ -309,7 +357,7 @@ export class MJComboboxComponent {
309
357
  this.SelectItem(items[this.HighlightedIndex]);
310
358
  }
311
359
  else {
312
- this.CommitValue();
360
+ this.commitValue();
313
361
  this.Close();
314
362
  }
315
363
  break;
@@ -344,6 +392,7 @@ export class MJComboboxComponent {
344
392
  registerOnChange(fn) { this.onChange = fn; }
345
393
  registerOnTouched(fn) { this.onTouched = fn; }
346
394
  setDisabledState(isDisabled) { this.formDisabled = isDisabled; this.syncDisabled(); }
395
+ ngAfterViewInit() { WarnIfUnnamed(this.ComboInput?.nativeElement, 'mj-combobox'); }
347
396
  ngOnDestroy() { this.Close(); }
348
397
  getDisplayText() {
349
398
  if (this.SelectedValue == null)
@@ -361,7 +410,7 @@ export class MJComboboxComponent {
361
410
  return -1;
362
411
  return this.FilteredItems.findIndex(item => this.IsItemSelected(item));
363
412
  }
364
- static ɵfac = function MJComboboxComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MJComboboxComponent)(); };
413
+ static ɵfac = /*@__PURE__*/ (() => { let ɵMJComboboxComponent_BaseFactory; return function MJComboboxComponent_Factory(__ngFactoryType__) { return (ɵMJComboboxComponent_BaseFactory || (ɵMJComboboxComponent_BaseFactory = i0.ɵɵgetInheritedFactory(MJComboboxComponent)))(__ngFactoryType__ || MJComboboxComponent); }; })();
365
414
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJComboboxComponent, selectors: [["mj-combobox"]], contentQueries: function MJComboboxComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) {
366
415
  i0.ɵɵcontentQuery(dirIndex, _c0, 5);
367
416
  } if (rf & 2) {
@@ -379,52 +428,71 @@ export class MJComboboxComponent {
379
428
  provide: NG_VALUE_ACCESSOR,
380
429
  useExisting: forwardRef(() => MJComboboxComponent),
381
430
  multi: true
382
- }])], decls: 9, vars: 15, consts: [["trigger", "", "overlayOrigin", "cdkOverlayOrigin"], ["comboInput", ""], ["cdkOverlayOrigin", "", "role", "combobox", "aria-haspopup", "listbox", 1, "mj-combobox"], ["type", "text", "autocomplete", "off", 1, "mj-input", "mj-combobox-input", 3, "input", "focus", "keydown", "blur", "placeholder", "value", "disabled"], ["type", "button", "tabindex", "-1", "aria-label", "Clear", 1, "mj-combobox-clear"], ["type", "button", "tabindex", "-1", 1, "mj-combobox-toggle", 3, "mousedown", "disabled"], [1, "fa-solid", "fa-chevron-down"], ["cdkConnectedOverlay", "", "cdkConnectedOverlayBackdropClass", "mj-dropdown-backdrop", 3, "backdropClick", "detach", "cdkConnectedOverlayOrigin", "cdkConnectedOverlayOpen", "cdkConnectedOverlayPositions", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHasBackdrop"], ["type", "button", "tabindex", "-1", "aria-label", "Clear", 1, "mj-combobox-clear", 3, "mousedown"], [1, "fa-solid", "fa-times"], ["role", "listbox", 1, "mj-dropdown-panel"], ["role", "option", 1, "mj-dropdown-option", 3, "mj-dropdown-option--selected", "mj-dropdown-option--highlighted"], [1, "mj-dropdown-no-data"], ["role", "option", 1, "mj-dropdown-option", 3, "mousedown"], [4, "ngTemplateOutlet", "ngTemplateOutletContext"]], template: function MJComboboxComponent_Template(rf, ctx) { if (rf & 1) {
431
+ }]), i0.ɵɵInheritDefinitionFeature], decls: 13, vars: 26, consts: [["trigger", "", "overlayOrigin", "cdkOverlayOrigin"], ["comboInput", ""], ["cdkOverlayOrigin", "", 1, "mj-combobox"], ["type", "text", "role", "combobox", "aria-haspopup", "listbox", "autocomplete", "off", 1, "mj-input", "mj-combobox-input", 3, "input", "focus", "keydown", "blur", "placeholder", "value", "disabled"], ["aria-hidden", "true", 1, "mj-combobox-sr-only"], ["type", "button", "tabindex", "-1", 1, "mj-combobox-clear"], ["type", "button", "tabindex", "-1", 1, "mj-combobox-toggle", 3, "mousedown", "disabled"], [1, "fa-solid", "fa-chevron-down"], ["cdkConnectedOverlay", "", "cdkConnectedOverlayBackdropClass", "mj-dropdown-backdrop", 3, "backdropClick", "detach", "cdkConnectedOverlayOrigin", "cdkConnectedOverlayOpen", "cdkConnectedOverlayPositions", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHasBackdrop"], ["type", "button", "tabindex", "-1", 1, "mj-combobox-clear", 3, "mousedown"], [1, "fa-solid", "fa-times"], ["role", "listbox", 1, "mj-dropdown-panel"], ["role", "option", 1, "mj-dropdown-option", 3, "mj-dropdown-option--selected", "mj-dropdown-option--highlighted"], [1, "mj-dropdown-no-data"], ["role", "option", 1, "mj-dropdown-option", 3, "mousedown"], [4, "ngTemplateOutlet", "ngTemplateOutletContext"]], template: function MJComboboxComponent_Template(rf, ctx) { if (rf & 1) {
383
432
  i0.ɵɵelementStart(0, "div", 2, 0)(3, "input", 3, 1);
384
433
  i0.ɵɵlistener("input", function MJComboboxComponent_Template_input_input_3_listener($event) { return ctx.OnInput($event); })("focus", function MJComboboxComponent_Template_input_focus_3_listener() { return ctx.OnFocus(); })("keydown", function MJComboboxComponent_Template_input_keydown_3_listener($event) { return ctx.OnKeyDown($event); })("blur", function MJComboboxComponent_Template_input_blur_3_listener() { return ctx.OnInputBlur(); });
385
434
  i0.ɵɵelementEnd();
386
- i0.ɵɵconditionalCreate(5, MJComboboxComponent_Conditional_5_Template, 2, 0, "button", 4);
387
- i0.ɵɵelementStart(6, "button", 5);
388
- i0.ɵɵlistener("mousedown", function MJComboboxComponent_Template_button_mousedown_6_listener($event) { return ctx.OnToggleMouseDown($event); });
389
- i0.ɵɵelement(7, "i", 6);
435
+ i0.ɵɵelementStart(5, "span", 4);
436
+ i0.ɵɵtext(6, "Clear");
437
+ i0.ɵɵelementEnd();
438
+ i0.ɵɵelementStart(7, "span", 4);
439
+ i0.ɵɵtext(8, "Show options for");
440
+ i0.ɵɵelementEnd();
441
+ i0.ɵɵconditionalCreate(9, MJComboboxComponent_Conditional_9_Template, 2, 2, "button", 5);
442
+ i0.ɵɵelementStart(10, "button", 6);
443
+ i0.ɵɵlistener("mousedown", function MJComboboxComponent_Template_button_mousedown_10_listener($event) { return ctx.OnToggleMouseDown($event); });
444
+ i0.ɵɵelement(11, "i", 7);
390
445
  i0.ɵɵelementEnd()();
391
- i0.ɵɵtemplate(8, MJComboboxComponent_ng_template_8_Template, 4, 1, "ng-template", 7);
392
- i0.ɵɵlistener("backdropClick", function MJComboboxComponent_Template_ng_template_backdropClick_8_listener() { return ctx.Close(); })("detach", function MJComboboxComponent_Template_ng_template_detach_8_listener() { return ctx.Close(); });
446
+ i0.ɵɵtemplate(12, MJComboboxComponent_ng_template_12_Template, 4, 4, "ng-template", 8);
447
+ i0.ɵɵlistener("backdropClick", function MJComboboxComponent_Template_ng_template_backdropClick_12_listener() { return ctx.Close(); })("detach", function MJComboboxComponent_Template_ng_template_detach_12_listener() { return ctx.Close(); });
393
448
  } if (rf & 2) {
394
449
  const overlayOrigin_r6 = i0.ɵɵreference(2);
395
450
  i0.ɵɵclassProp("mj-combobox--open", ctx.IsOpen)("mj-combobox--disabled", ctx.IsDisabled);
396
- i0.ɵɵattribute("aria-expanded", ctx.IsOpen);
397
451
  i0.ɵɵadvance(3);
398
452
  i0.ɵɵproperty("placeholder", ctx.Placeholder)("value", ctx.InputText)("disabled", ctx.IsDisabled);
453
+ i0.ɵɵattribute("id", ctx.InputId || null)("aria-label", ctx.AriaLabel || null)("aria-labelledby", ctx.AriaLabelledBy || null)("aria-describedby", ctx.AriaDescribedBy || null)("aria-expanded", ctx.IsOpen)("aria-autocomplete", ctx.Filterable ? "list" : "none")("aria-controls", ctx.IsOpen ? ctx.ListboxId : null)("aria-activedescendant", ctx.ActiveDescendantId);
454
+ i0.ɵɵadvance(2);
455
+ i0.ɵɵattribute("id", ctx.SecondaryWordId("clear-word"));
456
+ i0.ɵɵadvance(2);
457
+ i0.ɵɵattribute("id", ctx.SecondaryWordId("toggle-word"));
399
458
  i0.ɵɵadvance(2);
400
- i0.ɵɵconditional(ctx.InputText && !ctx.IsDisabled ? 5 : -1);
459
+ i0.ɵɵconditional(ctx.InputText && !ctx.IsDisabled ? 9 : -1);
401
460
  i0.ɵɵadvance();
402
461
  i0.ɵɵproperty("disabled", ctx.IsDisabled);
462
+ i0.ɵɵattribute("aria-labelledby", ctx.ToggleLabelledBy || null)("aria-label", ctx.ToggleLabelledBy ? null : ctx.ToggleLabel);
403
463
  i0.ɵɵadvance(2);
404
464
  i0.ɵɵproperty("cdkConnectedOverlayOrigin", overlayOrigin_r6)("cdkConnectedOverlayOpen", ctx.IsOpen)("cdkConnectedOverlayPositions", ctx.Positions)("cdkConnectedOverlayWidth", ctx.TriggerWidth)("cdkConnectedOverlayHasBackdrop", true);
405
- } }, dependencies: [NgTemplateOutlet, OverlayModule, i1.CdkConnectedOverlay, i1.CdkOverlayOrigin], encapsulation: 2 });
465
+ } }, dependencies: [NgTemplateOutlet, OverlayModule, i1.CdkConnectedOverlay, i1.CdkOverlayOrigin], styles: ["\n\n\n\n\n\n\n\n\n .mj-combobox-sr-only[_ngcontent-%COMP%] {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }"] });
406
466
  }
407
467
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJComboboxComponent, [{
408
468
  type: Component,
409
- args: [{
410
- selector: 'mj-combobox',
411
- standalone: true,
412
- imports: [NgTemplateOutlet, OverlayModule],
413
- template: `
469
+ args: [{ selector: 'mj-combobox', standalone: true, imports: [NgTemplateOutlet, OverlayModule], template: `
414
470
  <div
415
471
  class="mj-combobox"
416
472
  #trigger
417
473
  cdkOverlayOrigin
418
474
  #overlayOrigin="cdkOverlayOrigin"
419
475
  [class.mj-combobox--open]="IsOpen"
420
- [class.mj-combobox--disabled]="IsDisabled"
421
- role="combobox"
422
- [attr.aria-expanded]="IsOpen"
423
- aria-haspopup="listbox">
476
+ [class.mj-combobox--disabled]="IsDisabled">
477
+ <!--
478
+ role="combobox" belongs on the INPUT, not on this wrapper. The wrapper is not focusable, so
479
+ a screen reader never lands on it: its role, its name and its expanded state all went
480
+ unannounced while the element the user actually focused announced as a plain textbox.
481
+ -->
424
482
  <input
425
483
  #comboInput
426
484
  class="mj-input mj-combobox-input"
427
485
  type="text"
486
+ role="combobox"
487
+ [attr.id]="InputId || null"
488
+ [attr.aria-label]="AriaLabel || null"
489
+ [attr.aria-labelledby]="AriaLabelledBy || null"
490
+ [attr.aria-describedby]="AriaDescribedBy || null"
491
+ [attr.aria-expanded]="IsOpen"
492
+ aria-haspopup="listbox"
493
+ [attr.aria-autocomplete]="Filterable ? 'list' : 'none'"
494
+ [attr.aria-controls]="IsOpen ? ListboxId : null"
495
+ [attr.aria-activedescendant]="ActiveDescendantId"
428
496
  [placeholder]="Placeholder"
429
497
  [value]="InputText"
430
498
  [disabled]="IsDisabled"
@@ -433,13 +501,25 @@ export class MJComboboxComponent {
433
501
  (keydown)="OnKeyDown($event)"
434
502
  (blur)="OnInputBlur()"
435
503
  autocomplete="off" />
504
+ <!--
505
+ The fixed words live in hidden spans rather than in concatenated strings so the
506
+ AriaLabelledBy path can name these two buttons from the same visible label that names the
507
+ combobox: aria-labelledby takes an ID LIST, so the word plus the label's own text is
508
+ composed by the accessibility tree without this component ever seeing that text.
509
+ A hidden node that is DIRECTLY referenced by aria-labelledby is still included in the name
510
+ (accname §4.1), so aria-hidden keeps the word out of the reading order without costing the
511
+ composed name.
512
+ -->
513
+ <span class="mj-combobox-sr-only" aria-hidden="true" [attr.id]="SecondaryWordId('clear-word')">Clear</span>
514
+ <span class="mj-combobox-sr-only" aria-hidden="true" [attr.id]="SecondaryWordId('toggle-word')">Show options for</span>
436
515
  @if (InputText && !IsDisabled) {
437
516
  <button
438
517
  class="mj-combobox-clear"
439
518
  type="button"
440
519
  tabindex="-1"
441
520
  (mousedown)="OnClear($event)"
442
- aria-label="Clear">
521
+ [attr.aria-labelledby]="ClearLabelledBy || null"
522
+ [attr.aria-label]="ClearLabelledBy ? null : ClearLabel">
443
523
  <i class="fa-solid fa-times"></i>
444
524
  </button>
445
525
  }
@@ -448,7 +528,9 @@ export class MJComboboxComponent {
448
528
  type="button"
449
529
  tabindex="-1"
450
530
  [disabled]="IsDisabled"
451
- (mousedown)="OnToggleMouseDown($event)">
531
+ (mousedown)="OnToggleMouseDown($event)"
532
+ [attr.aria-labelledby]="ToggleLabelledBy || null"
533
+ [attr.aria-label]="ToggleLabelledBy ? null : ToggleLabel">
452
534
  <i class="fa-solid fa-chevron-down"></i>
453
535
  </button>
454
536
  </div>
@@ -463,13 +545,17 @@ export class MJComboboxComponent {
463
545
  cdkConnectedOverlayBackdropClass="mj-dropdown-backdrop"
464
546
  (backdropClick)="Close()"
465
547
  (detach)="Close()">
466
- <div class="mj-dropdown-panel" role="listbox">
548
+ <div class="mj-dropdown-panel" role="listbox"
549
+ [attr.id]="ListboxId"
550
+ [attr.aria-label]="AriaLabel || null"
551
+ [attr.aria-labelledby]="AriaLabelledBy || null">
467
552
  @for (item of FilteredItems; track TrackByIndex($index)) {
468
553
  <div
469
554
  class="mj-dropdown-option"
470
555
  [class.mj-dropdown-option--selected]="IsItemSelected(item)"
471
556
  [class.mj-dropdown-option--highlighted]="HighlightedIndex === $index"
472
557
  role="option"
558
+ [attr.id]="OptionId($index)"
473
559
  [attr.aria-selected]="IsItemSelected(item)"
474
560
  (mousedown)="SelectItem(item, $event)">
475
561
  @if (ItemTemplate) {
@@ -484,13 +570,11 @@ export class MJComboboxComponent {
484
570
  }
485
571
  </div>
486
572
  </ng-template>
487
- `,
488
- providers: [{
573
+ `, providers: [{
489
574
  provide: NG_VALUE_ACCESSOR,
490
575
  useExisting: forwardRef(() => MJComboboxComponent),
491
576
  multi: true
492
- }]
493
- }]
577
+ }], styles: ["\n /*\n * Component-scoped ON PURPOSE, unlike the rest of this control's styling, which ships as a global\n * stylesheet the host application imports. A host that skips that import gets unstyled chrome \u2014\n * survivable \u2014 but a hidden-word span that is not hidden renders its word as literal text in the\n * middle of the field. The rule that hides it therefore has to travel with the component.\n *\n * Not display:none or visibility:hidden \u2014 both remove the element from the accessibility tree,\n * which is the one thing this element exists to be in.\n */\n .mj-combobox-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n "] }]
494
578
  }], null, { Data: [{
495
579
  type: Input
496
580
  }], TextField: [{
@@ -524,5 +608,5 @@ export class MJComboboxComponent {
524
608
  type: HostBinding,
525
609
  args: ['class.mj-combobox-host']
526
610
  }] }); })();
527
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJComboboxComponent, { className: "MJComboboxComponent", filePath: "lib/combobox/combobox.component.ts", lineNumber: 126 }); })();
611
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJComboboxComponent, { className: "MJComboboxComponent", filePath: "lib/combobox/combobox.component.ts", lineNumber: 195 }); })();
528
612
  //# sourceMappingURL=combobox.component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"combobox.component.js","sourceRoot":"","sources":["../../../src/lib/combobox/combobox.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n Input,\n Output,\n EventEmitter,\n forwardRef,\n HostBinding,\n ElementRef,\n ViewChild,\n ContentChild,\n TemplateRef,\n OnDestroy,\n inject,\n ChangeDetectorRef\n} from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';\nimport { OverlayModule, ConnectedPosition } from '@angular/cdk/overlay';\n\n/**\n * mj-combobox — Editable dropdown with filtering and optional custom values.\n *\n * Replaces `<kendo-combobox>`. Unlike mj-dropdown, the trigger is a text input\n * that filters the list as you type. When AllowCustom is true, typed text that\n * doesn't match any item is emitted as the value.\n *\n * @example\n * ```html\n * <mj-combobox\n * [Data]=\"categories\"\n * TextField=\"text\"\n * ValueField=\"value\"\n * [(ngModel)]=\"selectedCategory\"\n * [ValuePrimitive]=\"true\"\n * [AllowCustom]=\"true\"\n * [Filterable]=\"true\"\n * Placeholder=\"Select or enter new...\">\n * </mj-combobox>\n * ```\n */\n@Component({\n selector: 'mj-combobox',\n standalone: true,\n imports: [NgTemplateOutlet, OverlayModule],\n template: `\n <div\n class=\"mj-combobox\"\n #trigger\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n [class.mj-combobox--open]=\"IsOpen\"\n [class.mj-combobox--disabled]=\"IsDisabled\"\n role=\"combobox\"\n [attr.aria-expanded]=\"IsOpen\"\n aria-haspopup=\"listbox\">\n <input\n #comboInput\n class=\"mj-input mj-combobox-input\"\n type=\"text\"\n [placeholder]=\"Placeholder\"\n [value]=\"InputText\"\n [disabled]=\"IsDisabled\"\n (input)=\"OnInput($event)\"\n (focus)=\"OnFocus()\"\n (keydown)=\"OnKeyDown($event)\"\n (blur)=\"OnInputBlur()\"\n autocomplete=\"off\" />\n @if (InputText && !IsDisabled) {\n <button\n class=\"mj-combobox-clear\"\n type=\"button\"\n tabindex=\"-1\"\n (mousedown)=\"OnClear($event)\"\n aria-label=\"Clear\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n <button\n class=\"mj-combobox-toggle\"\n type=\"button\"\n tabindex=\"-1\"\n [disabled]=\"IsDisabled\"\n (mousedown)=\"OnToggleMouseDown($event)\">\n <i class=\"fa-solid fa-chevron-down\"></i>\n </button>\n </div>\n\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"IsOpen\"\n [cdkConnectedOverlayPositions]=\"Positions\"\n [cdkConnectedOverlayWidth]=\"TriggerWidth\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"mj-dropdown-backdrop\"\n (backdropClick)=\"Close()\"\n (detach)=\"Close()\">\n <div class=\"mj-dropdown-panel\" role=\"listbox\">\n @for (item of FilteredItems; track TrackByIndex($index)) {\n <div\n class=\"mj-dropdown-option\"\n [class.mj-dropdown-option--selected]=\"IsItemSelected(item)\"\n [class.mj-dropdown-option--highlighted]=\"HighlightedIndex === $index\"\n role=\"option\"\n [attr.aria-selected]=\"IsItemSelected(item)\"\n (mousedown)=\"SelectItem(item, $event)\">\n @if (ItemTemplate) {\n <ng-container *ngTemplateOutlet=\"ItemTemplate; context: { $implicit: item }\"></ng-container>\n } @else {\n {{ GetItemText(item) }}\n }\n </div>\n }\n @if (FilteredItems.length === 0) {\n <div class=\"mj-dropdown-no-data\">No data found</div>\n }\n </div>\n </ng-template>\n `,\n providers: [{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => MJComboboxComponent),\n multi: true\n }]\n})\nexport class MJComboboxComponent implements ControlValueAccessor, OnDestroy {\n @Input()\n set Data(value: Record<string, unknown>[] | string[] | readonly unknown[] | null) {\n this._data = value;\n // Re-resolve display text when data arrives after writeValue\n if (this.SelectedValue != null && !this.InputText) {\n this.InputText = this.getDisplayText();\n }\n }\n get Data(): Record<string, unknown>[] | string[] | readonly unknown[] | null { return this._data; }\n private _data: Record<string, unknown>[] | string[] | readonly unknown[] | null = [];\n @Input() TextField = '';\n @Input() ValueField = '';\n @Input() Filterable = true;\n @Input() ValuePrimitive = false;\n /**\n * Host-driven disable. Composed with Angular Forms' `setDisabledState()` into `IsDisabled`\n * (the actual gate) — see `syncDisabled`. A setter, not a bare field, because this input is\n * routinely bound to an expression that changes over the control's lifetime, and the gate has\n * to follow it every time.\n */\n @Input()\n set Disabled(value: boolean) {\n this.disabledInput = value;\n this.syncDisabled();\n }\n get Disabled(): boolean {\n return this.disabledInput;\n }\n @Input() Placeholder = '';\n @Input() AllowCustom = false;\n\n @Output() ValueChange = new EventEmitter<unknown>();\n @Output() FilterChange = new EventEmitter<string>();\n\n @ContentChild('mjComboboxItem') ItemTemplate: TemplateRef<{ $implicit: unknown }> | null = null;\n\n @ViewChild('trigger') private triggerEl!: ElementRef<HTMLElement>;\n @ViewChild('comboInput') ComboInput!: ElementRef<HTMLInputElement>;\n\n @HostBinding('class.mj-combobox-host') readonly hostClass = true;\n\n private cdr = inject(ChangeDetectorRef);\n\n IsOpen = false;\n /**\n * The single gate on `Toggle()` / `Open()` — true when EITHER the `Disabled` input or Angular\n * Forms says so. Never assign it directly; go through `syncDisabled()`.\n */\n IsDisabled = false;\n HighlightedIndex = -1;\n SelectedValue: unknown = null;\n TriggerWidth = 0;\n InputText = '';\n\n Positions: ConnectedPosition[] = [\n { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top' },\n { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom' }\n ];\n\n private onChange: (value: unknown) => void = () => {};\n private onTouched: () => void = () => {};\n private isBlurring = false;\n\n get FilteredItems(): unknown[] {\n const data = (this.Data ?? []) as unknown[];\n if (!this.Filterable || !this.InputText) return data;\n const search = this.InputText.toLowerCase();\n return data.filter(item => this.GetItemText(item).toLowerCase().includes(search));\n }\n\n OnInput(event: Event): void {\n this.InputText = (event.target as HTMLInputElement).value;\n this.HighlightedIndex = 0;\n this.FilterChange.emit(this.InputText);\n if (!this.IsOpen) this.Open();\n this.cdr.detectChanges();\n }\n\n OnFocus(): void {\n if (!this.IsOpen) this.Open();\n }\n\n OnToggleMouseDown(event: Event): void {\n event.preventDefault(); // Prevent blur on the input\n this.Toggle();\n }\n\n OnClear(event: Event): void {\n event.preventDefault();\n event.stopPropagation();\n this.isBlurring = false;\n this.InputText = '';\n this.SelectedValue = null;\n this.onChange(null);\n this.ValueChange.emit(null);\n this.Close();\n this.cdr.detectChanges();\n }\n\n OnInputBlur(): void {\n this.isBlurring = true;\n setTimeout(() => {\n if (!this.isBlurring) return;\n this.isBlurring = false;\n this.CommitValue();\n this.Close();\n this.onTouched();\n }, 150);\n }\n\n Toggle(): void {\n if (this.IsDisabled) return;\n this.IsOpen ? this.Close() : this.Open();\n }\n\n Open(): void {\n if (this.IsDisabled || this.IsOpen) return;\n this.TriggerWidth = this.triggerEl?.nativeElement.offsetWidth ?? 200;\n this.IsOpen = true;\n this.HighlightedIndex = this.getSelectedIndex();\n this.cdr.detectChanges();\n }\n\n Close(): void {\n if (!this.IsOpen) return;\n this.resetPanelState();\n this.cdr.detectChanges();\n }\n\n /**\n * Panel state only — no change detection. Split out of `Close()` because `syncDisabled()` can\n * run from an @Input setter, i.e. DURING the parent's change-detection pass, where a nested\n * `detectChanges()` re-enters CD and trips NG0100 on the parent's own bindings.\n */\n private resetPanelState(): void {\n this.IsOpen = false;\n this.HighlightedIndex = -1;\n }\n\n /** Backing field for the `Disabled` input. */\n private disabledInput = false;\n /** The forms-driven disabled state, kept SEPARATE so neither source can stomp the other. */\n private formDisabled = false;\n\n /**\n * Recompute the gate from both of its sources. Called whenever either changes.\n *\n * `IsDisabled` is derived state, and the only thing that assigned it was `setDisabledState()`.\n * The forms-driven half was in fact fine — `setUpControl` also wires `registerOnDisabledChange`,\n * so that hook fires on every `control.disable()`/`enable()`, not just at registration. What had\n * no recompute path at all was the `Disabled` @Input: a plain field, so the gate froze at\n * whatever the first compose produced and every later change to the input was dropped.\n */\n private syncDisabled(): void {\n const disabled = this.disabledInput || this.formDisabled;\n if (disabled === this.IsDisabled) return;\n this.IsDisabled = disabled;\n if (disabled) this.resetPanelState();\n this.cdr.markForCheck();\n }\n\n SelectItem(item: unknown, event?: Event): void {\n event?.preventDefault();\n event?.stopPropagation();\n this.isBlurring = false;\n\n const value = this.ValuePrimitive && this.ValueField ? this.GetItemValue(item) : item;\n this.SelectedValue = value;\n this.InputText = this.GetItemText(item);\n this.onChange(value);\n this.ValueChange.emit(value);\n this.Close();\n this.cdr.detectChanges();\n }\n\n /** Commit the current input text as a value (for AllowCustom or matching item) */\n private CommitValue(): void {\n if (!this.InputText.trim()) {\n // Empty input — clear the value\n if (this.SelectedValue != null) {\n this.SelectedValue = null;\n this.onChange(null);\n this.ValueChange.emit(null);\n }\n return;\n }\n\n // Try to find a matching item\n const data = (this.Data ?? []) as unknown[];\n const match = data.find(item => this.GetItemText(item).toLowerCase() === this.InputText.toLowerCase());\n if (match) {\n const value = this.ValuePrimitive && this.ValueField ? this.GetItemValue(match) : match;\n if (value !== this.SelectedValue) {\n this.SelectedValue = value;\n this.InputText = this.GetItemText(match);\n this.onChange(value);\n this.ValueChange.emit(value);\n }\n } else if (this.AllowCustom) {\n // No match but custom values allowed — emit the typed text\n this.SelectedValue = this.InputText;\n this.onChange(this.InputText);\n this.ValueChange.emit(this.InputText);\n } else {\n // No match and no custom — revert to previous display\n this.InputText = this.getDisplayText();\n }\n }\n\n IsItemSelected(item: unknown): boolean {\n if (this.SelectedValue == null) return false;\n if (this.ValuePrimitive && this.ValueField) return this.GetItemValue(item) === this.SelectedValue;\n if (this.ValueField && typeof item === 'object' && typeof this.SelectedValue === 'object')\n return this.GetItemValue(item) === this.GetItemValue(this.SelectedValue);\n return item === this.SelectedValue;\n }\n\n OnKeyDown(event: KeyboardEvent): void {\n const items = this.FilteredItems;\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n if (!this.IsOpen) this.Open();\n else this.HighlightedIndex = Math.min(this.HighlightedIndex + 1, items.length - 1);\n break;\n case 'ArrowUp':\n event.preventDefault();\n if (this.IsOpen) this.HighlightedIndex = Math.max(this.HighlightedIndex - 1, 0);\n break;\n case 'Enter':\n event.preventDefault();\n if (this.IsOpen && this.HighlightedIndex >= 0 && this.HighlightedIndex < items.length) {\n this.SelectItem(items[this.HighlightedIndex]);\n } else {\n this.CommitValue();\n this.Close();\n }\n break;\n case 'Escape':\n event.preventDefault();\n this.InputText = this.getDisplayText();\n this.Close();\n break;\n }\n }\n\n GetItemText(item: unknown): string {\n if (item == null) return '';\n if (typeof item === 'string') return item;\n if (this.TextField && typeof item === 'object') return String((item as Record<string, unknown>)[this.TextField] ?? '');\n return String(item);\n }\n\n GetItemValue(item: unknown): unknown {\n if (typeof item === 'string') return item;\n if (this.ValueField && typeof item === 'object' && item != null) return (item as Record<string, unknown>)[this.ValueField];\n return item;\n }\n\n TrackByIndex(index: number): number { return index; }\n\n writeValue(value: unknown): void {\n this.SelectedValue = value;\n this.InputText = this.getDisplayText();\n }\n\n registerOnChange(fn: (value: unknown) => void): void { this.onChange = fn; }\n registerOnTouched(fn: () => void): void { this.onTouched = fn; }\n setDisabledState(isDisabled: boolean): void { this.formDisabled = isDisabled; this.syncDisabled(); }\n ngOnDestroy(): void { this.Close(); }\n\n private getDisplayText(): string {\n if (this.SelectedValue == null) return '';\n if (this.ValuePrimitive && this.ValueField) {\n const found = ((this.Data ?? []) as unknown[]).find(item => this.GetItemValue(item) === this.SelectedValue);\n return found ? this.GetItemText(found) : String(this.SelectedValue);\n }\n if (typeof this.SelectedValue === 'string') return this.SelectedValue;\n return this.GetItemText(this.SelectedValue as Record<string, unknown>);\n }\n\n private getSelectedIndex(): number {\n if (this.SelectedValue == null) return -1;\n return this.FilteredItems.findIndex(item => this.IsItemSelected(item));\n }\n}\n"]}
1
+ {"version":3,"file":"combobox.component.js","sourceRoot":"","sources":["../../../src/lib/combobox/combobox.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AfterViewInit,\n Component,\n Input,\n Output,\n EventEmitter,\n forwardRef,\n HostBinding,\n ElementRef,\n ViewChild,\n ContentChild,\n TemplateRef,\n OnDestroy,\n inject,\n ChangeDetectorRef\n} from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';\nimport { OverlayModule, ConnectedPosition } from '@angular/cdk/overlay';\nimport { MJNamedControlBase } from '../a11y/named-control.base';\nimport { WarnIfUnnamed } from '../a11y/unnamed-control-guard';\n\n/**\n * mj-combobox — Editable dropdown with filtering and optional custom values.\n *\n * Replaces `<kendo-combobox>`. Unlike mj-dropdown, the trigger is a text input\n * that filters the list as you type. When AllowCustom is true, typed text that\n * doesn't match any item is emitted as the value.\n *\n * Every combobox needs an ACCESSIBLE NAME, or it announces as \"combobox, collapsed\" with no hint of\n * what it selects (WCAG 2.1 4.1.2). Use {@link MJNamedControlBase.AriaLabelledBy} when a visible\n * label already exists and {@link MJNamedControlBase.AriaLabel} when none does. Here the trigger is\n * a real `<input>`, so {@link MJNamedControlBase.InputId} IS a valid `<label for>` target — unlike\n * `mj-dropdown`, whose trigger is a div.\n *\n * @example With a visible label (preferred)\n * ```html\n * <span id=\"category-label\">Category</span>\n * <mj-combobox AriaLabelledBy=\"category-label\" [Data]=\"categories\" TextField=\"text\"\n * ValueField=\"value\" [(ngModel)]=\"selectedCategory\" [ValuePrimitive]=\"true\">\n * </mj-combobox>\n * ```\n *\n * @example\n * ```html\n * <mj-combobox\n * [Data]=\"categories\"\n * TextField=\"text\"\n * ValueField=\"value\"\n * [(ngModel)]=\"selectedCategory\"\n * [ValuePrimitive]=\"true\"\n * [AllowCustom]=\"true\"\n * [Filterable]=\"true\"\n * Placeholder=\"Select or enter new...\">\n * </mj-combobox>\n * ```\n */\n@Component({\n selector: 'mj-combobox',\n standalone: true,\n imports: [NgTemplateOutlet, OverlayModule],\n template: `\n <div\n class=\"mj-combobox\"\n #trigger\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n [class.mj-combobox--open]=\"IsOpen\"\n [class.mj-combobox--disabled]=\"IsDisabled\">\n <!--\n role=\"combobox\" belongs on the INPUT, not on this wrapper. The wrapper is not focusable, so\n a screen reader never lands on it: its role, its name and its expanded state all went\n unannounced while the element the user actually focused announced as a plain textbox.\n -->\n <input\n #comboInput\n class=\"mj-input mj-combobox-input\"\n type=\"text\"\n role=\"combobox\"\n [attr.id]=\"InputId || null\"\n [attr.aria-label]=\"AriaLabel || null\"\n [attr.aria-labelledby]=\"AriaLabelledBy || null\"\n [attr.aria-describedby]=\"AriaDescribedBy || null\"\n [attr.aria-expanded]=\"IsOpen\"\n aria-haspopup=\"listbox\"\n [attr.aria-autocomplete]=\"Filterable ? 'list' : 'none'\"\n [attr.aria-controls]=\"IsOpen ? ListboxId : null\"\n [attr.aria-activedescendant]=\"ActiveDescendantId\"\n [placeholder]=\"Placeholder\"\n [value]=\"InputText\"\n [disabled]=\"IsDisabled\"\n (input)=\"OnInput($event)\"\n (focus)=\"OnFocus()\"\n (keydown)=\"OnKeyDown($event)\"\n (blur)=\"OnInputBlur()\"\n autocomplete=\"off\" />\n <!--\n The fixed words live in hidden spans rather than in concatenated strings so the\n AriaLabelledBy path can name these two buttons from the same visible label that names the\n combobox: aria-labelledby takes an ID LIST, so the word plus the label's own text is\n composed by the accessibility tree without this component ever seeing that text.\n A hidden node that is DIRECTLY referenced by aria-labelledby is still included in the name\n (accname §4.1), so aria-hidden keeps the word out of the reading order without costing the\n composed name.\n -->\n <span class=\"mj-combobox-sr-only\" aria-hidden=\"true\" [attr.id]=\"SecondaryWordId('clear-word')\">Clear</span>\n <span class=\"mj-combobox-sr-only\" aria-hidden=\"true\" [attr.id]=\"SecondaryWordId('toggle-word')\">Show options for</span>\n @if (InputText && !IsDisabled) {\n <button\n class=\"mj-combobox-clear\"\n type=\"button\"\n tabindex=\"-1\"\n (mousedown)=\"OnClear($event)\"\n [attr.aria-labelledby]=\"ClearLabelledBy || null\"\n [attr.aria-label]=\"ClearLabelledBy ? null : ClearLabel\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n <button\n class=\"mj-combobox-toggle\"\n type=\"button\"\n tabindex=\"-1\"\n [disabled]=\"IsDisabled\"\n (mousedown)=\"OnToggleMouseDown($event)\"\n [attr.aria-labelledby]=\"ToggleLabelledBy || null\"\n [attr.aria-label]=\"ToggleLabelledBy ? null : ToggleLabel\">\n <i class=\"fa-solid fa-chevron-down\"></i>\n </button>\n </div>\n\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"IsOpen\"\n [cdkConnectedOverlayPositions]=\"Positions\"\n [cdkConnectedOverlayWidth]=\"TriggerWidth\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"mj-dropdown-backdrop\"\n (backdropClick)=\"Close()\"\n (detach)=\"Close()\">\n <div class=\"mj-dropdown-panel\" role=\"listbox\"\n [attr.id]=\"ListboxId\"\n [attr.aria-label]=\"AriaLabel || null\"\n [attr.aria-labelledby]=\"AriaLabelledBy || null\">\n @for (item of FilteredItems; track TrackByIndex($index)) {\n <div\n class=\"mj-dropdown-option\"\n [class.mj-dropdown-option--selected]=\"IsItemSelected(item)\"\n [class.mj-dropdown-option--highlighted]=\"HighlightedIndex === $index\"\n role=\"option\"\n [attr.id]=\"OptionId($index)\"\n [attr.aria-selected]=\"IsItemSelected(item)\"\n (mousedown)=\"SelectItem(item, $event)\">\n @if (ItemTemplate) {\n <ng-container *ngTemplateOutlet=\"ItemTemplate; context: { $implicit: item }\"></ng-container>\n } @else {\n {{ GetItemText(item) }}\n }\n </div>\n }\n @if (FilteredItems.length === 0) {\n <div class=\"mj-dropdown-no-data\">No data found</div>\n }\n </div>\n </ng-template>\n `,\n styles: [`\n /*\n * Component-scoped ON PURPOSE, unlike the rest of this control's styling, which ships as a global\n * stylesheet the host application imports. A host that skips that import gets unstyled chrome —\n * survivable — but a hidden-word span that is not hidden renders its word as literal text in the\n * middle of the field. The rule that hides it therefore has to travel with the component.\n *\n * Not display:none or visibility:hidden — both remove the element from the accessibility tree,\n * which is the one thing this element exists to be in.\n */\n .mj-combobox-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n `],\n providers: [{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => MJComboboxComponent),\n multi: true\n }]\n})\nexport class MJComboboxComponent extends MJNamedControlBase implements ControlValueAccessor, AfterViewInit, OnDestroy {\n @Input()\n set Data(value: Record<string, unknown>[] | string[] | readonly unknown[] | null) {\n this._data = value;\n // Re-resolve display text when data arrives after writeValue\n if (this.SelectedValue != null && !this.InputText) {\n this.InputText = this.getDisplayText();\n }\n }\n get Data(): Record<string, unknown>[] | string[] | readonly unknown[] | null { return this._data; }\n private _data: Record<string, unknown>[] | string[] | readonly unknown[] | null = [];\n @Input() TextField = '';\n @Input() ValueField = '';\n @Input() Filterable = true;\n @Input() ValuePrimitive = false;\n /**\n * Host-driven disable. Composed with Angular Forms' `setDisabledState()` into `IsDisabled`\n * (the actual gate) — see `syncDisabled`. A setter, not a bare field, because this input is\n * routinely bound to an expression that changes over the control's lifetime, and the gate has\n * to follow it every time.\n */\n @Input()\n set Disabled(value: boolean) {\n this.disabledInput = value;\n this.syncDisabled();\n }\n get Disabled(): boolean {\n return this.disabledInput;\n }\n @Input() Placeholder = '';\n @Input() AllowCustom = false;\n\n @Output() ValueChange = new EventEmitter<unknown>();\n @Output() FilterChange = new EventEmitter<string>();\n\n @ContentChild('mjComboboxItem') ItemTemplate: TemplateRef<{ $implicit: unknown }> | null = null;\n\n @ViewChild('trigger') private triggerEl!: ElementRef<HTMLElement>;\n @ViewChild('comboInput') ComboInput!: ElementRef<HTMLInputElement>;\n\n @HostBinding('class.mj-combobox-host') readonly hostClass = true;\n\n private cdr = inject(ChangeDetectorRef);\n\n IsOpen = false;\n /**\n * The single gate on `Toggle()` / `Open()` — true when EITHER the `Disabled` input or Angular\n * Forms says so. Never assign it directly; go through `syncDisabled()`.\n */\n IsDisabled = false;\n HighlightedIndex = -1;\n SelectedValue: unknown = null;\n TriggerWidth = 0;\n InputText = '';\n\n Positions: ConnectedPosition[] = [\n { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top' },\n { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom' }\n ];\n\n /**\n * Id of the popup listbox, so the input can point `aria-controls` at it while open — the half of\n * the combobox pattern that makes \"collapsed/expanded\" refer to something a screen reader can\n * find.\n */\n get ListboxId(): string { return `mj-combobox-listbox-${this.NamedControlId}`; }\n\n /** Id of one rendered option, referenced by {@link ActiveDescendantId}. */\n OptionId(index: number): string { return `mj-combobox-option-${this.NamedControlId}-${index}`; }\n\n /**\n * `aria-activedescendant` for the input: the option the arrow keys have moved to. Focus stays in\n * the input the whole time, so without this the highlight is a CSS class and nothing else — a\n * screen-reader user arrowing through the list hears no change at all.\n */\n get ActiveDescendantId(): string | null {\n const active = this.IsOpen && this.HighlightedIndex >= 0 && this.HighlightedIndex < this.FilteredItems.length;\n return active ? this.OptionId(this.HighlightedIndex) : null;\n }\n\n /** `aria-labelledby` id list for the clear button when a VISIBLE label names the combobox. */\n get ClearLabelledBy(): string { return this.SecondaryLabelledBy('clear-word'); }\n\n /** `aria-label` for the clear button in the no-visible-label case. */\n get ClearLabel(): string { return this.SecondaryLabel('Clear', 'Clear'); }\n\n /** `aria-labelledby` id list for the toggle button when a VISIBLE label names the combobox. */\n get ToggleLabelledBy(): string { return this.SecondaryLabelledBy('toggle-word'); }\n\n /**\n * `aria-label` for the toggle button in the no-visible-label case. It had no name at all before\n * #4116 — an unnamed button next to a named input, which announces as \"button\".\n */\n get ToggleLabel(): string { return this.SecondaryLabel('Show options for', 'Show options'); }\n\n private onChange: (value: unknown) => void = () => {};\n private onTouched: () => void = () => {};\n private isBlurring = false;\n\n get FilteredItems(): unknown[] {\n const data = (this.Data ?? []) as unknown[];\n if (!this.Filterable || !this.InputText) return data;\n const search = this.InputText.toLowerCase();\n return data.filter(item => this.GetItemText(item).toLowerCase().includes(search));\n }\n\n OnInput(event: Event): void {\n this.InputText = (event.target as HTMLInputElement).value;\n this.HighlightedIndex = 0;\n this.FilterChange.emit(this.InputText);\n if (!this.IsOpen) this.Open();\n this.cdr.detectChanges();\n }\n\n OnFocus(): void {\n if (!this.IsOpen) this.Open();\n }\n\n OnToggleMouseDown(event: Event): void {\n event.preventDefault(); // Prevent blur on the input\n this.Toggle();\n }\n\n OnClear(event: Event): void {\n event.preventDefault();\n event.stopPropagation();\n this.isBlurring = false;\n this.InputText = '';\n this.SelectedValue = null;\n this.onChange(null);\n this.ValueChange.emit(null);\n this.Close();\n this.cdr.detectChanges();\n }\n\n OnInputBlur(): void {\n this.isBlurring = true;\n setTimeout(() => {\n if (!this.isBlurring) return;\n this.isBlurring = false;\n this.commitValue();\n this.Close();\n this.onTouched();\n }, 150);\n }\n\n Toggle(): void {\n if (this.IsDisabled) return;\n this.IsOpen ? this.Close() : this.Open();\n }\n\n Open(): void {\n if (this.IsDisabled || this.IsOpen) return;\n this.TriggerWidth = this.triggerEl?.nativeElement.offsetWidth ?? 200;\n this.IsOpen = true;\n this.HighlightedIndex = this.getSelectedIndex();\n this.cdr.detectChanges();\n }\n\n Close(): void {\n if (!this.IsOpen) return;\n this.resetPanelState();\n this.cdr.detectChanges();\n }\n\n /**\n * Panel state only — no change detection. Split out of `Close()` because `syncDisabled()` can\n * run from an @Input setter, i.e. DURING the parent's change-detection pass, where a nested\n * `detectChanges()` re-enters CD and trips NG0100 on the parent's own bindings.\n */\n private resetPanelState(): void {\n this.IsOpen = false;\n this.HighlightedIndex = -1;\n }\n\n /** Backing field for the `Disabled` input. */\n private disabledInput = false;\n /** The forms-driven disabled state, kept SEPARATE so neither source can stomp the other. */\n private formDisabled = false;\n\n /**\n * Recompute the gate from both of its sources. Called whenever either changes.\n *\n * `IsDisabled` is derived state, and the only thing that assigned it was `setDisabledState()`.\n * The forms-driven half was in fact fine — `setUpControl` also wires `registerOnDisabledChange`,\n * so that hook fires on every `control.disable()`/`enable()`, not just at registration. What had\n * no recompute path at all was the `Disabled` @Input: a plain field, so the gate froze at\n * whatever the first compose produced and every later change to the input was dropped.\n */\n private syncDisabled(): void {\n const disabled = this.disabledInput || this.formDisabled;\n if (disabled === this.IsDisabled) return;\n this.IsDisabled = disabled;\n if (disabled) this.resetPanelState();\n this.cdr.markForCheck();\n }\n\n SelectItem(item: unknown, event?: Event): void {\n event?.preventDefault();\n event?.stopPropagation();\n this.isBlurring = false;\n\n const value = this.ValuePrimitive && this.ValueField ? this.GetItemValue(item) : item;\n this.SelectedValue = value;\n this.InputText = this.GetItemText(item);\n this.onChange(value);\n this.ValueChange.emit(value);\n this.Close();\n this.cdr.detectChanges();\n }\n\n /** Commit the current input text as a value (for AllowCustom or matching item) */\n private commitValue(): void {\n if (!this.InputText.trim()) {\n // Empty input — clear the value\n if (this.SelectedValue != null) {\n this.SelectedValue = null;\n this.onChange(null);\n this.ValueChange.emit(null);\n }\n return;\n }\n\n // Try to find a matching item\n const data = (this.Data ?? []) as unknown[];\n const match = data.find(item => this.GetItemText(item).toLowerCase() === this.InputText.toLowerCase());\n if (match) {\n const value = this.ValuePrimitive && this.ValueField ? this.GetItemValue(match) : match;\n if (value !== this.SelectedValue) {\n this.SelectedValue = value;\n this.InputText = this.GetItemText(match);\n this.onChange(value);\n this.ValueChange.emit(value);\n }\n } else if (this.AllowCustom) {\n // No match but custom values allowed — emit the typed text\n this.SelectedValue = this.InputText;\n this.onChange(this.InputText);\n this.ValueChange.emit(this.InputText);\n } else {\n // No match and no custom — revert to previous display\n this.InputText = this.getDisplayText();\n }\n }\n\n IsItemSelected(item: unknown): boolean {\n if (this.SelectedValue == null) return false;\n if (this.ValuePrimitive && this.ValueField) return this.GetItemValue(item) === this.SelectedValue;\n if (this.ValueField && typeof item === 'object' && typeof this.SelectedValue === 'object')\n return this.GetItemValue(item) === this.GetItemValue(this.SelectedValue);\n return item === this.SelectedValue;\n }\n\n OnKeyDown(event: KeyboardEvent): void {\n const items = this.FilteredItems;\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n if (!this.IsOpen) this.Open();\n else this.HighlightedIndex = Math.min(this.HighlightedIndex + 1, items.length - 1);\n break;\n case 'ArrowUp':\n event.preventDefault();\n if (this.IsOpen) this.HighlightedIndex = Math.max(this.HighlightedIndex - 1, 0);\n break;\n case 'Enter':\n event.preventDefault();\n if (this.IsOpen && this.HighlightedIndex >= 0 && this.HighlightedIndex < items.length) {\n this.SelectItem(items[this.HighlightedIndex]);\n } else {\n this.commitValue();\n this.Close();\n }\n break;\n case 'Escape':\n event.preventDefault();\n this.InputText = this.getDisplayText();\n this.Close();\n break;\n }\n }\n\n GetItemText(item: unknown): string {\n if (item == null) return '';\n if (typeof item === 'string') return item;\n if (this.TextField && typeof item === 'object') return String((item as Record<string, unknown>)[this.TextField] ?? '');\n return String(item);\n }\n\n GetItemValue(item: unknown): unknown {\n if (typeof item === 'string') return item;\n if (this.ValueField && typeof item === 'object' && item != null) return (item as Record<string, unknown>)[this.ValueField];\n return item;\n }\n\n TrackByIndex(index: number): number { return index; }\n\n writeValue(value: unknown): void {\n this.SelectedValue = value;\n this.InputText = this.getDisplayText();\n }\n\n registerOnChange(fn: (value: unknown) => void): void { this.onChange = fn; }\n registerOnTouched(fn: () => void): void { this.onTouched = fn; }\n setDisabledState(isDisabled: boolean): void { this.formDisabled = isDisabled; this.syncDisabled(); }\n ngAfterViewInit(): void { WarnIfUnnamed(this.ComboInput?.nativeElement, 'mj-combobox'); }\n ngOnDestroy(): void { this.Close(); }\n\n private getDisplayText(): string {\n if (this.SelectedValue == null) return '';\n if (this.ValuePrimitive && this.ValueField) {\n const found = ((this.Data ?? []) as unknown[]).find(item => this.GetItemValue(item) === this.SelectedValue);\n return found ? this.GetItemText(found) : String(this.SelectedValue);\n }\n if (typeof this.SelectedValue === 'string') return this.SelectedValue;\n return this.GetItemText(this.SelectedValue as Record<string, unknown>);\n }\n\n private getSelectedIndex(): number {\n if (this.SelectedValue == null) return -1;\n return this.FilteredItems.findIndex(item => this.IsItemSelected(item));\n }\n}\n"]}
@@ -90,9 +90,15 @@ export declare class MJConfirmDialogComponent {
90
90
  * to suppress it), otherwise the per-type default.
91
91
  */
92
92
  get ResolvedIcon(): string;
93
+ OnConfirm(): void;
94
+ /** @deprecated Use {@link OnConfirm}. */
93
95
  onConfirm(): void;
96
+ OnCancel(): void;
97
+ /** @deprecated Use {@link OnCancel}. */
94
98
  onCancel(): void;
95
99
  /** Backdrop / Esc / ✕ from the underlying dialog — ignored while Processing. */
100
+ OnDismiss(): void;
101
+ /** @deprecated Use {@link OnDismiss}. */
96
102
  onDismiss(): void;
97
103
  private close;
98
104
  static ɵfac: i0.ɵɵFactoryDeclaration<MJConfirmDialogComponent, never>;
@@ -1 +1 @@
1
- {"version":3,"file":"confirm-dialog.component.d.ts","sourceRoot":"","sources":["../../../src/lib/confirm-dialog/confirm-dialog.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAmC,MAAM,eAAe,CAAC;;AAIzF;;;;;;;;GAQG;AACH,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;AAS5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,qBA+Ea,wBAAwB;IACnC,oEAAoE;IAC3D,OAAO,UAAS;IACzB,sFAAsF;IAC5E,aAAa,wBAA+B;IAEtD,6CAA6C;IACpC,KAAK,SAAa;IAE3B,qFAAqF;IAC5E,OAAO,SAAmB;IAEnC,2EAA2E;IAClE,aAAa,SAAM;IAE5B,4FAA4F;IACnF,IAAI,EAAE,mBAAmB,CAAa;IAE/C,gCAAgC;IACvB,WAAW,SAAa;IAEjC,4BAA4B;IACnB,UAAU,SAAY;IAE/B;;;OAGG;IACM,IAAI,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEpC;;;;OAIG;IACqC,UAAU,UAAS;IAE3D,gFAAgF;IACtE,SAAS,qBAA4B;IAE/C,yFAAyF;IAC/E,SAAS,qBAA4B;IAE/C,kFAAkF;IAClF,IAAI,cAAc,IAAI,SAAS,GAAG,QAAQ,CAEzC;IAED;;;OAGG;IACH,IAAI,YAAY,IAAI,MAAM,CAKzB;IAED,SAAS,IAAI,IAAI;IAIjB,QAAQ,IAAI,IAAI;IAKhB,gFAAgF;IAChF,SAAS,IAAI,IAAI;IAOjB,OAAO,CAAC,KAAK;yCA5EF,wBAAwB;2CAAxB,wBAAwB;yCAiFum+L,OAAQ;CADnp+L"}
1
+ {"version":3,"file":"confirm-dialog.component.d.ts","sourceRoot":"","sources":["../../../src/lib/confirm-dialog/confirm-dialog.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAmC,MAAM,eAAe,CAAC;;AAIzF;;;;;;;;GAQG;AACH,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;AAS5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,qBA+Ea,wBAAwB;IACnC,oEAAoE;IAC3D,OAAO,UAAS;IACzB,sFAAsF;IAC5E,aAAa,wBAA+B;IAEtD,6CAA6C;IACpC,KAAK,SAAa;IAE3B,qFAAqF;IAC5E,OAAO,SAAmB;IAEnC,2EAA2E;IAClE,aAAa,SAAM;IAE5B,4FAA4F;IACnF,IAAI,EAAE,mBAAmB,CAAa;IAE/C,gCAAgC;IACvB,WAAW,SAAa;IAEjC,4BAA4B;IACnB,UAAU,SAAY;IAE/B;;;OAGG;IACM,IAAI,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEpC;;;;OAIG;IACqC,UAAU,UAAS;IAE3D,gFAAgF;IACtE,SAAS,qBAA4B;IAE/C,yFAAyF;IAC/E,SAAS,qBAA4B;IAE/C,kFAAkF;IAClF,IAAI,cAAc,IAAI,SAAS,GAAG,QAAQ,CAEzC;IAED;;;OAGG;IACH,IAAI,YAAY,IAAI,MAAM,CAKzB;IAED,SAAS,IAAI,IAAI;IAIjB,yCAAyC;IACzC,SAAS,IAAI,IAAI;IAIjB,QAAQ,IAAI,IAAI;IAKhB,wCAAwC;IACxC,QAAQ,IAAI,IAAI;IAIhB,gFAAgF;IAChF,SAAS,IAAI,IAAI;IAOjB,yCAAyC;IACzC,SAAS,IAAI,IAAI;IAIjB,OAAO,CAAC,KAAK;yCA3FF,wBAAwB;2CAAxB,wBAAwB;yCAgG8z9L,OAAQ;CAD129L"}
@@ -123,19 +123,31 @@ export class MJConfirmDialogComponent {
123
123
  }
124
124
  return DEFAULT_ICONS[this.Type] ?? DEFAULT_ICONS.default;
125
125
  }
126
- onConfirm() {
126
+ OnConfirm() {
127
127
  this.Confirmed.emit();
128
128
  }
129
- onCancel() {
129
+ /** @deprecated Use {@link OnConfirm}. */
130
+ onConfirm() {
131
+ return this.OnConfirm();
132
+ }
133
+ OnCancel() {
130
134
  this.close();
131
135
  this.Cancelled.emit();
132
136
  }
137
+ /** @deprecated Use {@link OnCancel}. */
138
+ onCancel() {
139
+ return this.OnCancel();
140
+ }
133
141
  /** Backdrop / Esc / ✕ from the underlying dialog — ignored while Processing. */
134
- onDismiss() {
142
+ OnDismiss() {
135
143
  if (this.Processing) {
136
144
  return;
137
145
  }
138
- this.onCancel();
146
+ this.OnCancel();
147
+ }
148
+ /** @deprecated Use {@link OnDismiss}. */
149
+ onDismiss() {
150
+ return this.OnDismiss();
139
151
  }
140
152
  close() {
141
153
  this.Visible = false;