@beshkari/mb-ui 0.5.0-alpha.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 (124) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -0
  3. package/esm2022/beshkari-mb-ui.mjs +5 -0
  4. package/esm2022/lib/accordion/mb-accordion.component.mjs +37 -0
  5. package/esm2022/lib/alert/mb-alert.component.mjs +28 -0
  6. package/esm2022/lib/avatar/mb-avatar.component.mjs +32 -0
  7. package/esm2022/lib/badge/mb-badge.component.mjs +28 -0
  8. package/esm2022/lib/breadcrumb/mb-breadcrumb.component.mjs +23 -0
  9. package/esm2022/lib/button/mb-button.component.mjs +48 -0
  10. package/esm2022/lib/card/mb-card.component.mjs +28 -0
  11. package/esm2022/lib/checkbox/mb-checkbox.component.mjs +75 -0
  12. package/esm2022/lib/chip/mb-chip.component.mjs +33 -0
  13. package/esm2022/lib/combobox/mb-combobox.component.mjs +235 -0
  14. package/esm2022/lib/confirm/mb-confirm-dialog.component.mjs +44 -0
  15. package/esm2022/lib/confirm/mb-confirm.service.mjs +39 -0
  16. package/esm2022/lib/confirm/mb-confirm.types.mjs +2 -0
  17. package/esm2022/lib/datepicker/mb-datepicker.component.mjs +382 -0
  18. package/esm2022/lib/dialog/mb-dialog-container.component.mjs +37 -0
  19. package/esm2022/lib/dialog/mb-dialog-ref.mjs +17 -0
  20. package/esm2022/lib/dialog/mb-dialog.component.mjs +87 -0
  21. package/esm2022/lib/dialog/mb-dialog.service.mjs +61 -0
  22. package/esm2022/lib/dialog/mb-dialog.tokens.mjs +3 -0
  23. package/esm2022/lib/divider/mb-divider.component.mjs +18 -0
  24. package/esm2022/lib/drawer/mb-drawer.component.mjs +34 -0
  25. package/esm2022/lib/dropdown/mb-dropdown.component.mjs +24 -0
  26. package/esm2022/lib/empty/mb-empty.component.mjs +27 -0
  27. package/esm2022/lib/form-field/mb-form-field.component.mjs +24 -0
  28. package/esm2022/lib/grid/mb-grid-data.mjs +266 -0
  29. package/esm2022/lib/grid/mb-grid-export.mjs +36 -0
  30. package/esm2022/lib/grid/mb-grid-messages.mjs +10 -0
  31. package/esm2022/lib/grid/mb-grid-state-adapter.mjs +97 -0
  32. package/esm2022/lib/grid/mb-grid-state.service.mjs +47 -0
  33. package/esm2022/lib/grid/mb-grid-template.directive.mjs +109 -0
  34. package/esm2022/lib/grid/mb-grid.component.mjs +998 -0
  35. package/esm2022/lib/grid/mb-grid.types.mjs +2 -0
  36. package/esm2022/lib/i18n/mb-ui-locale.mjs +18 -0
  37. package/esm2022/lib/i18n/mb-ui-messages-en.mjs +103 -0
  38. package/esm2022/lib/i18n/mb-ui-messages-fa.mjs +103 -0
  39. package/esm2022/lib/i18n/mb-ui-messages.mjs +34 -0
  40. package/esm2022/lib/i18n/mb-ui-messages.types.mjs +2 -0
  41. package/esm2022/lib/i18n/provide-mb-ui.mjs +20 -0
  42. package/esm2022/lib/input/mb-input.component.mjs +86 -0
  43. package/esm2022/lib/pagination/mb-pagination.component.mjs +47 -0
  44. package/esm2022/lib/progress/mb-progress.component.mjs +29 -0
  45. package/esm2022/lib/radio/mb-radio-group.component.mjs +63 -0
  46. package/esm2022/lib/select/mb-select.component.mjs +73 -0
  47. package/esm2022/lib/skeleton/mb-skeleton.component.mjs +24 -0
  48. package/esm2022/lib/spinner/mb-spinner.component.mjs +21 -0
  49. package/esm2022/lib/switch/mb-switch.component.mjs +59 -0
  50. package/esm2022/lib/tabs/mb-tabs.component.mjs +31 -0
  51. package/esm2022/lib/textarea/mb-textarea.component.mjs +83 -0
  52. package/esm2022/lib/toast/mb-toast-container.component.mjs +21 -0
  53. package/esm2022/lib/toast/mb-toast.service.mjs +47 -0
  54. package/esm2022/lib/toast/mb-toast.types.mjs +2 -0
  55. package/esm2022/lib/tokens/mb-ui-config.token.mjs +11 -0
  56. package/esm2022/lib/tooltip/mb-tooltip.directive.mjs +48 -0
  57. package/esm2022/lib/utils/coerce.mjs +10 -0
  58. package/esm2022/lib/utils/mb-jalali-date.mjs +120 -0
  59. package/esm2022/public-api.mjs +56 -0
  60. package/fesm2022/beshkari-mb-ui.mjs +3828 -0
  61. package/fesm2022/beshkari-mb-ui.mjs.map +1 -0
  62. package/index.d.ts +5 -0
  63. package/lib/accordion/mb-accordion.component.d.ts +19 -0
  64. package/lib/alert/mb-alert.component.d.ts +12 -0
  65. package/lib/avatar/mb-avatar.component.d.ts +12 -0
  66. package/lib/badge/mb-badge.component.d.ts +12 -0
  67. package/lib/breadcrumb/mb-breadcrumb.component.d.ts +15 -0
  68. package/lib/button/mb-button.component.d.ts +19 -0
  69. package/lib/card/mb-card.component.d.ts +10 -0
  70. package/lib/checkbox/mb-checkbox.component.d.ts +25 -0
  71. package/lib/chip/mb-chip.component.d.ts +12 -0
  72. package/lib/combobox/mb-combobox.component.d.ts +65 -0
  73. package/lib/confirm/mb-confirm-dialog.component.d.ts +17 -0
  74. package/lib/confirm/mb-confirm.service.d.ts +12 -0
  75. package/lib/confirm/mb-confirm.types.d.ts +10 -0
  76. package/lib/datepicker/mb-datepicker.component.d.ts +87 -0
  77. package/lib/dialog/mb-dialog-container.component.d.ts +14 -0
  78. package/lib/dialog/mb-dialog-ref.d.ts +9 -0
  79. package/lib/dialog/mb-dialog.component.d.ts +27 -0
  80. package/lib/dialog/mb-dialog.service.d.ts +14 -0
  81. package/lib/dialog/mb-dialog.tokens.d.ts +13 -0
  82. package/lib/divider/mb-divider.component.d.ts +7 -0
  83. package/lib/drawer/mb-drawer.component.d.ts +14 -0
  84. package/lib/dropdown/mb-dropdown.component.d.ts +10 -0
  85. package/lib/empty/mb-empty.component.d.ts +10 -0
  86. package/lib/form-field/mb-form-field.component.d.ts +9 -0
  87. package/lib/grid/mb-grid-data.d.ts +20 -0
  88. package/lib/grid/mb-grid-export.d.ts +9 -0
  89. package/lib/grid/mb-grid-messages.d.ts +6 -0
  90. package/lib/grid/mb-grid-state-adapter.d.ts +24 -0
  91. package/lib/grid/mb-grid-state.service.d.ts +10 -0
  92. package/lib/grid/mb-grid-template.directive.d.ts +43 -0
  93. package/lib/grid/mb-grid.component.d.ts +233 -0
  94. package/lib/grid/mb-grid.types.d.ts +255 -0
  95. package/lib/i18n/mb-ui-locale.d.ts +2 -0
  96. package/lib/i18n/mb-ui-messages-en.d.ts +3 -0
  97. package/lib/i18n/mb-ui-messages-fa.d.ts +3 -0
  98. package/lib/i18n/mb-ui-messages.d.ts +5 -0
  99. package/lib/i18n/mb-ui-messages.types.d.ts +90 -0
  100. package/lib/i18n/provide-mb-ui.d.ts +7 -0
  101. package/lib/input/mb-input.component.d.ts +30 -0
  102. package/lib/pagination/mb-pagination.component.d.ts +16 -0
  103. package/lib/progress/mb-progress.component.d.ts +11 -0
  104. package/lib/radio/mb-radio-group.component.d.ts +30 -0
  105. package/lib/select/mb-select.component.d.ts +31 -0
  106. package/lib/skeleton/mb-skeleton.component.d.ts +9 -0
  107. package/lib/spinner/mb-spinner.component.d.ts +9 -0
  108. package/lib/switch/mb-switch.component.d.ts +22 -0
  109. package/lib/tabs/mb-tabs.component.d.ts +19 -0
  110. package/lib/textarea/mb-textarea.component.d.ts +29 -0
  111. package/lib/toast/mb-toast-container.component.d.ts +10 -0
  112. package/lib/toast/mb-toast.service.d.ts +15 -0
  113. package/lib/toast/mb-toast.types.d.ts +8 -0
  114. package/lib/tokens/mb-ui-config.token.d.ts +12 -0
  115. package/lib/tooltip/mb-tooltip.directive.d.ts +13 -0
  116. package/lib/utils/coerce.d.ts +3 -0
  117. package/lib/utils/mb-jalali-date.d.ts +18 -0
  118. package/package.json +50 -0
  119. package/public-api.d.ts +55 -0
  120. package/theme/_components.scss +19 -0
  121. package/theme/_dark.scss +1 -0
  122. package/theme/_density.scss +1 -0
  123. package/theme/_tokens.scss +57 -0
  124. package/theme/mb-ui.scss +28 -0
@@ -0,0 +1,3828 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, makeEnvironmentProviders, EventEmitter, HostBinding, Output, Input, ChangeDetectionStrategy, Component, forwardRef, HostListener, Directive, Injectable, ContentChild, ContentChildren, Inject, ViewChild, Injector } from '@angular/core';
3
+ import { NgClass, NgIf, NgFor, NgStyle, NgTemplateOutlet, DecimalPipe, KeyValuePipe, AsyncPipe } from '@angular/common';
4
+ import * as i1 from '@angular/forms';
5
+ import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
6
+ import * as i3 from '@angular/cdk/drag-drop';
7
+ import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
8
+ import { ScrollingModule } from '@angular/cdk/scrolling';
9
+ import { Subject, BehaviorSubject, firstValueFrom } from 'rxjs';
10
+ import * as i1$1 from '@angular/cdk/portal';
11
+ import { PortalModule, CdkPortalOutlet, ComponentPortal } from '@angular/cdk/portal';
12
+ import * as i1$3 from '@angular/cdk/a11y';
13
+ import { CdkTrapFocus, A11yModule } from '@angular/cdk/a11y';
14
+ import * as i1$2 from '@angular/cdk/overlay';
15
+ import { OverlayConfig } from '@angular/cdk/overlay';
16
+ import { take, filter } from 'rxjs/operators';
17
+
18
+ const MB_UI_DEFAULT_CONFIG = {
19
+ direction: 'ltr',
20
+ density: 'comfortable',
21
+ locale: 'en-US',
22
+ };
23
+ const MB_UI_CONFIG = new InjectionToken('MB_UI_CONFIG', {
24
+ providedIn: 'root',
25
+ factory: () => MB_UI_DEFAULT_CONFIG,
26
+ });
27
+
28
+ const LOCALE_ALIASES = {
29
+ fa: 'fa-IR',
30
+ 'fa-ir': 'fa-IR',
31
+ 'fa_IR': 'fa-IR',
32
+ en: 'en-US',
33
+ 'en-us': 'en-US',
34
+ 'en_US': 'en-US',
35
+ };
36
+ function normalizeMbUiLocale(locale) {
37
+ const normalized = locale.trim().replace('_', '-');
38
+ const alias = LOCALE_ALIASES[normalized.toLowerCase()];
39
+ if (alias)
40
+ return alias;
41
+ if (normalized === 'fa-IR' || normalized === 'en-US')
42
+ return normalized;
43
+ return normalized.toLowerCase().startsWith('fa') ? 'fa-IR' : 'en-US';
44
+ }
45
+
46
+ const MB_GRID_MESSAGES_EN = {
47
+ noRecords: 'No records available',
48
+ loading: 'Loading...',
49
+ groupLabel: 'Group by:',
50
+ groupHint: 'Use the column menu to group by a column',
51
+ columns: 'Columns',
52
+ resetColumns: 'Reset columns',
53
+ addRow: 'Add row',
54
+ searchPlaceholder: 'Quick search...',
55
+ exportCsv: 'CSV',
56
+ exportExcel: 'Excel',
57
+ exportJson: 'JSON',
58
+ reset: 'Reset',
59
+ selectAllRows: 'Select all visible rows',
60
+ selectRow: 'Select row',
61
+ columnMenu: 'Column menu',
62
+ sort: 'Sort',
63
+ groupByColumn: 'Group by this column',
64
+ removeGroup: 'Remove grouping',
65
+ filter: 'Filter',
66
+ clearFilter: 'Clear filter',
67
+ filterValuePlaceholder: 'Filter value',
68
+ lockColumn: 'Lock column',
69
+ unlockColumn: 'Unlock column',
70
+ hideColumn: 'Hide column',
71
+ save: 'Save',
72
+ cancel: 'Cancel',
73
+ edit: 'Edit',
74
+ delete: 'Delete',
75
+ deleteConfirm: 'Are you sure you want to delete this row?',
76
+ fieldRequired: (title) => `${title} is required`,
77
+ commandColumn: 'Actions',
78
+ pagerFirst: 'First',
79
+ pagerPrevious: 'Previous',
80
+ pagerNext: 'Next',
81
+ pagerLast: 'Last',
82
+ pagerInfo: (from, to, total) => `Showing ${from} to ${to} of ${total}`,
83
+ yes: 'Yes',
84
+ no: 'No',
85
+ operatorLabels: {
86
+ contains: 'Contains',
87
+ doesNotContain: 'Does not contain',
88
+ startsWith: 'Starts with',
89
+ endsWith: 'Ends with',
90
+ eq: 'Equals',
91
+ neq: 'Does not equal',
92
+ gt: 'Greater than',
93
+ gte: 'Greater than or equal',
94
+ lt: 'Less than',
95
+ lte: 'Less than or equal',
96
+ isNull: 'Is null',
97
+ isNotNull: 'Is not null',
98
+ isEmpty: 'Is empty',
99
+ isNotEmpty: 'Is not empty',
100
+ between: 'Between',
101
+ },
102
+ };
103
+ const MB_UI_MESSAGES_EN = {
104
+ grid: MB_GRID_MESSAGES_EN,
105
+ common: {
106
+ close: 'Close',
107
+ clear: 'Clear',
108
+ yes: 'Yes',
109
+ no: 'No',
110
+ },
111
+ confirm: {
112
+ title: 'Confirm action',
113
+ okText: 'Confirm',
114
+ cancelText: 'Cancel',
115
+ deleteTitle: 'Delete item',
116
+ deleteOkText: 'Delete',
117
+ ariaLabel: 'Confirm dialog',
118
+ },
119
+ form: {
120
+ selectPlaceholder: 'Select an option',
121
+ searchPlaceholder: 'Search...',
122
+ selectDefaultItem: 'Select',
123
+ datePlaceholder: 'Pick a date',
124
+ noMatches: 'No matches found',
125
+ },
126
+ empty: {
127
+ title: 'No records available',
128
+ },
129
+ datepicker: {
130
+ placeholder: 'Pick a date',
131
+ clear: 'Clear date',
132
+ pickDate: 'Pick a date',
133
+ today: 'Today',
134
+ weekdayNames: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
135
+ monthNamesGregorian: [
136
+ 'January', 'February', 'March', 'April', 'May', 'June',
137
+ 'July', 'August', 'September', 'October', 'November', 'December',
138
+ ],
139
+ },
140
+ pagination: {
141
+ ariaLabel: 'Pagination',
142
+ },
143
+ dialog: {
144
+ ariaLabel: 'Dialog',
145
+ close: 'Close',
146
+ },
147
+ };
148
+
149
+ const MB_GRID_MESSAGES_FA = {
150
+ noRecords: 'داده‌ای برای نمایش وجود ندارد',
151
+ loading: 'در حال بارگذاری...',
152
+ groupLabel: 'گروه‌بندی:',
153
+ groupHint: 'از منوی ستون، گزینه گروه‌بندی را انتخاب کن',
154
+ columns: 'ستون‌ها',
155
+ resetColumns: 'بازنشانی ستون‌ها',
156
+ addRow: 'افزودن ردیف',
157
+ searchPlaceholder: 'جستجوی سریع...',
158
+ exportCsv: 'CSV',
159
+ exportExcel: 'Excel',
160
+ exportJson: 'JSON',
161
+ reset: 'بازنشانی',
162
+ selectAllRows: 'انتخاب همه ردیف‌های قابل مشاهده',
163
+ selectRow: 'انتخاب ردیف',
164
+ columnMenu: 'منوی ستون',
165
+ sort: 'مرتب‌سازی',
166
+ groupByColumn: 'گروه‌بندی براساس این ستون',
167
+ removeGroup: 'حذف گروه‌بندی',
168
+ filter: 'فیلتر',
169
+ clearFilter: 'پاک کردن فیلتر',
170
+ filterValuePlaceholder: 'مقدار فیلتر',
171
+ lockColumn: 'قفل کردن ستون',
172
+ unlockColumn: 'آزاد کردن ستون',
173
+ hideColumn: 'مخفی کردن ستون',
174
+ save: 'ذخیره',
175
+ cancel: 'لغو',
176
+ edit: 'ویرایش',
177
+ delete: 'حذف',
178
+ deleteConfirm: 'آیا از حذف این ردیف مطمئن هستید؟',
179
+ fieldRequired: (title) => `${title} الزامی است`,
180
+ commandColumn: 'عملیات',
181
+ pagerFirst: 'اول',
182
+ pagerPrevious: 'قبلی',
183
+ pagerNext: 'بعدی',
184
+ pagerLast: 'آخر',
185
+ pagerInfo: (from, to, total) => `نمایش ${from} تا ${to} از ${total}`,
186
+ yes: 'بله',
187
+ no: 'خیر',
188
+ operatorLabels: {
189
+ contains: 'شامل باشد',
190
+ doesNotContain: 'شامل نباشد',
191
+ startsWith: 'شروع شود با',
192
+ endsWith: 'تمام شود با',
193
+ eq: 'برابر باشد',
194
+ neq: 'برابر نباشد',
195
+ gt: 'بزرگ‌تر از',
196
+ gte: 'بزرگ‌تر یا مساوی',
197
+ lt: 'کوچک‌تر از',
198
+ lte: 'کوچک‌تر یا مساوی',
199
+ isNull: 'خالی/Null باشد',
200
+ isNotNull: 'خالی/Null نباشد',
201
+ isEmpty: 'رشته خالی باشد',
202
+ isNotEmpty: 'رشته خالی نباشد',
203
+ between: 'بین دو مقدار',
204
+ },
205
+ };
206
+ const MB_UI_MESSAGES_FA = {
207
+ grid: MB_GRID_MESSAGES_FA,
208
+ common: {
209
+ close: 'بستن',
210
+ clear: 'پاک کردن',
211
+ yes: 'بله',
212
+ no: 'خیر',
213
+ },
214
+ confirm: {
215
+ title: 'تأیید عملیات',
216
+ okText: 'تأیید',
217
+ cancelText: 'انصراف',
218
+ deleteTitle: 'حذف آیتم',
219
+ deleteOkText: 'حذف',
220
+ ariaLabel: 'پنجره تأیید',
221
+ },
222
+ form: {
223
+ selectPlaceholder: 'انتخاب کنید',
224
+ searchPlaceholder: 'جستجو...',
225
+ selectDefaultItem: 'انتخاب',
226
+ datePlaceholder: 'انتخاب تاریخ',
227
+ noMatches: 'موردی یافت نشد',
228
+ },
229
+ empty: {
230
+ title: 'داده‌ای برای نمایش وجود ندارد',
231
+ },
232
+ datepicker: {
233
+ placeholder: 'انتخاب تاریخ',
234
+ clear: 'پاک کردن تاریخ',
235
+ pickDate: 'انتخاب تاریخ',
236
+ today: 'امروز',
237
+ weekdayNames: ['ش', 'ی', 'د', 'س', 'چ', 'پ', 'ج'],
238
+ monthNamesGregorian: [
239
+ 'ژانویه', 'فوریه', 'مارس', 'آوریل', 'مه', 'ژوئن',
240
+ 'ژوئیه', 'اوت', 'سپتامبر', 'اکتبر', 'نوامبر', 'دسامبر',
241
+ ],
242
+ },
243
+ pagination: {
244
+ ariaLabel: 'صفحه‌بندی',
245
+ },
246
+ dialog: {
247
+ ariaLabel: 'پنجره',
248
+ close: 'بستن',
249
+ },
250
+ };
251
+
252
+ const LOCALE_MESSAGES = {
253
+ 'en-US': MB_UI_MESSAGES_EN,
254
+ 'fa-IR': MB_UI_MESSAGES_FA,
255
+ };
256
+ function getMbUiMessages(locale) {
257
+ return LOCALE_MESSAGES[normalizeMbUiLocale(locale)] ?? MB_UI_MESSAGES_EN;
258
+ }
259
+ function mergeMbUiMessages(base, overrides) {
260
+ if (!overrides)
261
+ return base;
262
+ return {
263
+ grid: { ...base.grid, ...overrides.grid, operatorLabels: { ...base.grid.operatorLabels, ...overrides.grid?.operatorLabels } },
264
+ common: { ...base.common, ...overrides.common },
265
+ confirm: { ...base.confirm, ...overrides.confirm },
266
+ form: { ...base.form, ...overrides.form },
267
+ empty: { ...base.empty, ...overrides.empty },
268
+ datepicker: { ...base.datepicker, ...overrides.datepicker },
269
+ pagination: { ...base.pagination, ...overrides.pagination },
270
+ dialog: { ...base.dialog, ...overrides.dialog },
271
+ };
272
+ }
273
+ const MB_UI_MESSAGES = new InjectionToken('MB_UI_MESSAGES', {
274
+ providedIn: 'root',
275
+ factory: () => {
276
+ const config = inject(MB_UI_CONFIG);
277
+ return getMbUiMessages(config.locale);
278
+ },
279
+ });
280
+
281
+ /** @deprecated Use `MB_UI_MESSAGES` or locale bundles from `i18n/`. */
282
+ const MB_GRID_DEFAULT_MESSAGES_FA = MB_GRID_MESSAGES_FA;
283
+ const MB_GRID_MESSAGES = new InjectionToken('MB_GRID_MESSAGES', {
284
+ providedIn: 'root',
285
+ factory: () => inject(MB_UI_MESSAGES).grid,
286
+ });
287
+
288
+ function provideMbUi(options = {}) {
289
+ const locale = normalizeMbUiLocale(options.locale ?? MB_UI_DEFAULT_CONFIG.locale);
290
+ const config = {
291
+ direction: options.direction ?? MB_UI_DEFAULT_CONFIG.direction,
292
+ density: options.density ?? MB_UI_DEFAULT_CONFIG.density,
293
+ locale,
294
+ };
295
+ const messages = mergeMbUiMessages(getMbUiMessages(locale), options.messages);
296
+ return makeEnvironmentProviders([
297
+ { provide: MB_UI_CONFIG, useValue: config },
298
+ { provide: MB_UI_MESSAGES, useValue: messages },
299
+ { provide: MB_GRID_MESSAGES, useFactory: () => messages.grid, deps: [] },
300
+ ]);
301
+ }
302
+
303
+ function coerceBooleanProperty(value) {
304
+ return value != null && `${value}` !== 'false';
305
+ }
306
+ function isNil(value) {
307
+ return value === null || value === undefined;
308
+ }
309
+ function normalizeText(value) {
310
+ return isNil(value) ? '' : String(value).trim().toLowerCase();
311
+ }
312
+
313
+ class MbButtonComponent {
314
+ variant = 'primary';
315
+ size = 'md';
316
+ type = 'button';
317
+ disabled = false;
318
+ loading = false;
319
+ fullWidth = false;
320
+ ariaLabel;
321
+ clicked = new EventEmitter();
322
+ hostClass = 'mb-button-host';
323
+ onClick(event) {
324
+ if (this.disabled || this.loading) {
325
+ event.preventDefault();
326
+ event.stopPropagation();
327
+ return;
328
+ }
329
+ this.clicked.emit(event);
330
+ }
331
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
332
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbButtonComponent, isStandalone: true, selector: "mb-button", inputs: { variant: "variant", size: "size", type: "type", disabled: "disabled", loading: "loading", fullWidth: "fullWidth", ariaLabel: "ariaLabel" }, outputs: { clicked: "clicked" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<button\n class=\"mb-button\"\n [attr.type]=\"type\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-busy]=\"loading\"\n [disabled]=\"disabled || loading\"\n (click)=\"onClick($event)\"\n [ngClass]=\"[\n 'mb-button--' + variant,\n 'mb-button--' + size,\n fullWidth ? 'mb-button--full' : ''\n ]\">\n <span *ngIf=\"loading\" class=\"mb-button__spinner\" aria-hidden=\"true\"></span>\n <span class=\"mb-button__content\"><ng-content></ng-content></span>\n</button>\n", styles: [":host{display:inline-flex}.mb-button{appearance:none;border:1px solid transparent;border-radius:var(--mb-radius-md);display:inline-flex;align-items:center;justify-content:center;gap:.5rem;font-family:var(--mb-font-family);font-weight:650;line-height:1;cursor:pointer;transition:background var(--mb-transition-fast),color var(--mb-transition-fast),border-color var(--mb-transition-fast),box-shadow var(--mb-transition-fast),transform var(--mb-transition-fast);white-space:nowrap;-webkit-user-select:none;user-select:none}.mb-button:focus-visible{outline:none;box-shadow:var(--mb-focus-ring)}.mb-button:active:not(:disabled){transform:translateY(1px)}.mb-button:disabled{cursor:not-allowed;opacity:.62}.mb-button--sm{min-height:2rem;padding:0 .75rem;font-size:.8125rem}.mb-button--md{min-height:2.5rem;padding:0 1rem;font-size:.875rem}.mb-button--lg{min-height:3rem;padding:0 1.25rem;font-size:.9375rem}.mb-button--full{width:100%}.mb-button--primary{background:var(--mb-color-primary);color:#fff}.mb-button--primary:hover:not(:disabled){background:var(--mb-color-primary-hover)}.mb-button--secondary{background:var(--mb-color-surface-elevated);border-color:var(--mb-color-border);color:var(--mb-color-text)}.mb-button--secondary:hover:not(:disabled){border-color:var(--mb-color-border-strong)}.mb-button--danger{background:var(--mb-color-danger);color:#fff}.mb-button--ghost{background:transparent;color:var(--mb-color-text)}.mb-button--ghost:hover:not(:disabled){background:var(--mb-color-surface)}.mb-button--link{background:transparent;color:var(--mb-color-primary);padding-inline:.25rem;min-height:auto}.mb-button__spinner{inline-size:1em;block-size:1em;border:2px solid currentColor;border-block-start-color:transparent;border-radius:999px;animation:mb-button-spin .72s linear infinite}@keyframes mb-button-spin{to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
333
+ }
334
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbButtonComponent, decorators: [{
335
+ type: Component,
336
+ args: [{ selector: 'mb-button', standalone: true, imports: [NgClass, NgIf], changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n class=\"mb-button\"\n [attr.type]=\"type\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-busy]=\"loading\"\n [disabled]=\"disabled || loading\"\n (click)=\"onClick($event)\"\n [ngClass]=\"[\n 'mb-button--' + variant,\n 'mb-button--' + size,\n fullWidth ? 'mb-button--full' : ''\n ]\">\n <span *ngIf=\"loading\" class=\"mb-button__spinner\" aria-hidden=\"true\"></span>\n <span class=\"mb-button__content\"><ng-content></ng-content></span>\n</button>\n", styles: [":host{display:inline-flex}.mb-button{appearance:none;border:1px solid transparent;border-radius:var(--mb-radius-md);display:inline-flex;align-items:center;justify-content:center;gap:.5rem;font-family:var(--mb-font-family);font-weight:650;line-height:1;cursor:pointer;transition:background var(--mb-transition-fast),color var(--mb-transition-fast),border-color var(--mb-transition-fast),box-shadow var(--mb-transition-fast),transform var(--mb-transition-fast);white-space:nowrap;-webkit-user-select:none;user-select:none}.mb-button:focus-visible{outline:none;box-shadow:var(--mb-focus-ring)}.mb-button:active:not(:disabled){transform:translateY(1px)}.mb-button:disabled{cursor:not-allowed;opacity:.62}.mb-button--sm{min-height:2rem;padding:0 .75rem;font-size:.8125rem}.mb-button--md{min-height:2.5rem;padding:0 1rem;font-size:.875rem}.mb-button--lg{min-height:3rem;padding:0 1.25rem;font-size:.9375rem}.mb-button--full{width:100%}.mb-button--primary{background:var(--mb-color-primary);color:#fff}.mb-button--primary:hover:not(:disabled){background:var(--mb-color-primary-hover)}.mb-button--secondary{background:var(--mb-color-surface-elevated);border-color:var(--mb-color-border);color:var(--mb-color-text)}.mb-button--secondary:hover:not(:disabled){border-color:var(--mb-color-border-strong)}.mb-button--danger{background:var(--mb-color-danger);color:#fff}.mb-button--ghost{background:transparent;color:var(--mb-color-text)}.mb-button--ghost:hover:not(:disabled){background:var(--mb-color-surface)}.mb-button--link{background:transparent;color:var(--mb-color-primary);padding-inline:.25rem;min-height:auto}.mb-button__spinner{inline-size:1em;block-size:1em;border:2px solid currentColor;border-block-start-color:transparent;border-radius:999px;animation:mb-button-spin .72s linear infinite}@keyframes mb-button-spin{to{transform:rotate(360deg)}}\n"] }]
337
+ }], propDecorators: { variant: [{
338
+ type: Input
339
+ }], size: [{
340
+ type: Input
341
+ }], type: [{
342
+ type: Input
343
+ }], disabled: [{
344
+ type: Input
345
+ }], loading: [{
346
+ type: Input
347
+ }], fullWidth: [{
348
+ type: Input
349
+ }], ariaLabel: [{
350
+ type: Input
351
+ }], clicked: [{
352
+ type: Output
353
+ }], hostClass: [{
354
+ type: HostBinding,
355
+ args: ['class']
356
+ }] } });
357
+
358
+ class MbInputComponent {
359
+ messages = inject(MB_UI_MESSAGES);
360
+ type = 'text';
361
+ placeholder = '';
362
+ size = 'md';
363
+ disabled = false;
364
+ readonly = false;
365
+ invalid = false;
366
+ id;
367
+ name;
368
+ clearable = false;
369
+ valueChange = new EventEmitter();
370
+ value = '';
371
+ onChange = () => undefined;
372
+ onTouched = () => undefined;
373
+ writeValue(value) {
374
+ this.value = value ?? '';
375
+ }
376
+ registerOnChange(fn) {
377
+ this.onChange = fn;
378
+ }
379
+ registerOnTouched(fn) {
380
+ this.onTouched = fn;
381
+ }
382
+ setDisabledState(isDisabled) {
383
+ this.disabled = isDisabled;
384
+ }
385
+ handleInput(value) {
386
+ this.value = value;
387
+ this.onChange(value);
388
+ this.valueChange.emit(value);
389
+ }
390
+ blur() {
391
+ this.onTouched();
392
+ }
393
+ clear() {
394
+ if (this.disabled || this.readonly)
395
+ return;
396
+ this.handleInput('');
397
+ }
398
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
399
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbInputComponent, isStandalone: true, selector: "mb-input", inputs: { type: "type", placeholder: "placeholder", size: "size", disabled: "disabled", readonly: "readonly", invalid: "invalid", id: "id", name: "name", clearable: "clearable" }, outputs: { valueChange: "valueChange" }, providers: [
400
+ {
401
+ provide: NG_VALUE_ACCESSOR,
402
+ useExisting: forwardRef(() => MbInputComponent),
403
+ multi: true,
404
+ },
405
+ ], ngImport: i0, template: "<div class=\"mb-input-wrap\" [ngClass]=\"['mb-input-wrap--' + size, invalid ? 'mb-input-wrap--invalid' : '']\">\n <input\n class=\"mb-input\"\n [id]=\"id\"\n [attr.name]=\"name || null\"\n [attr.type]=\"type\"\n [attr.placeholder]=\"placeholder\"\n [attr.aria-invalid]=\"invalid\"\n [disabled]=\"disabled\"\n [readonly]=\"readonly\"\n [ngModel]=\"value\"\n (ngModelChange)=\"handleInput($event)\"\n (blur)=\"blur()\" />\n <button\n *ngIf=\"clearable && value\"\n class=\"mb-input__clear\"\n type=\"button\"\n [attr.aria-label]=\"messages.common.clear\"\n (click)=\"clear()\">\n \u00D7\n </button>\n</div>\n", styles: [".mb-input-wrap{display:inline-flex;align-items:center;inline-size:100%;border:1px solid var(--mb-color-border);border-radius:var(--mb-radius-md);background:var(--mb-color-bg);color:var(--mb-color-text);transition:border-color var(--mb-transition-fast),box-shadow var(--mb-transition-fast),background var(--mb-transition-fast)}.mb-input-wrap:focus-within{border-color:var(--mb-color-primary);box-shadow:var(--mb-focus-ring)}.mb-input-wrap--invalid{border-color:var(--mb-color-danger)}.mb-input-wrap--sm{min-height:2rem}.mb-input-wrap--md{min-height:2.5rem}.mb-input-wrap--lg{min-height:3rem}.mb-input{inline-size:100%;min-inline-size:0;border:0;outline:0;background:transparent;color:inherit;font:inherit;padding-inline:.875rem;block-size:100%}.mb-input::placeholder{color:var(--mb-color-muted)}.mb-input:disabled{cursor:not-allowed;color:var(--mb-color-disabled)}.mb-input__clear{border:0;background:transparent;color:var(--mb-color-muted);cursor:pointer;font-size:1.25rem;padding-inline:.75rem}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
406
+ }
407
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbInputComponent, decorators: [{
408
+ type: Component,
409
+ args: [{ selector: 'mb-input', standalone: true, imports: [FormsModule, NgClass, NgIf], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
410
+ {
411
+ provide: NG_VALUE_ACCESSOR,
412
+ useExisting: forwardRef(() => MbInputComponent),
413
+ multi: true,
414
+ },
415
+ ], template: "<div class=\"mb-input-wrap\" [ngClass]=\"['mb-input-wrap--' + size, invalid ? 'mb-input-wrap--invalid' : '']\">\n <input\n class=\"mb-input\"\n [id]=\"id\"\n [attr.name]=\"name || null\"\n [attr.type]=\"type\"\n [attr.placeholder]=\"placeholder\"\n [attr.aria-invalid]=\"invalid\"\n [disabled]=\"disabled\"\n [readonly]=\"readonly\"\n [ngModel]=\"value\"\n (ngModelChange)=\"handleInput($event)\"\n (blur)=\"blur()\" />\n <button\n *ngIf=\"clearable && value\"\n class=\"mb-input__clear\"\n type=\"button\"\n [attr.aria-label]=\"messages.common.clear\"\n (click)=\"clear()\">\n \u00D7\n </button>\n</div>\n", styles: [".mb-input-wrap{display:inline-flex;align-items:center;inline-size:100%;border:1px solid var(--mb-color-border);border-radius:var(--mb-radius-md);background:var(--mb-color-bg);color:var(--mb-color-text);transition:border-color var(--mb-transition-fast),box-shadow var(--mb-transition-fast),background var(--mb-transition-fast)}.mb-input-wrap:focus-within{border-color:var(--mb-color-primary);box-shadow:var(--mb-focus-ring)}.mb-input-wrap--invalid{border-color:var(--mb-color-danger)}.mb-input-wrap--sm{min-height:2rem}.mb-input-wrap--md{min-height:2.5rem}.mb-input-wrap--lg{min-height:3rem}.mb-input{inline-size:100%;min-inline-size:0;border:0;outline:0;background:transparent;color:inherit;font:inherit;padding-inline:.875rem;block-size:100%}.mb-input::placeholder{color:var(--mb-color-muted)}.mb-input:disabled{cursor:not-allowed;color:var(--mb-color-disabled)}.mb-input__clear{border:0;background:transparent;color:var(--mb-color-muted);cursor:pointer;font-size:1.25rem;padding-inline:.75rem}\n"] }]
416
+ }], propDecorators: { type: [{
417
+ type: Input
418
+ }], placeholder: [{
419
+ type: Input
420
+ }], size: [{
421
+ type: Input
422
+ }], disabled: [{
423
+ type: Input
424
+ }], readonly: [{
425
+ type: Input
426
+ }], invalid: [{
427
+ type: Input
428
+ }], id: [{
429
+ type: Input
430
+ }], name: [{
431
+ type: Input
432
+ }], clearable: [{
433
+ type: Input
434
+ }], valueChange: [{
435
+ type: Output
436
+ }] } });
437
+
438
+ class MbFormFieldComponent {
439
+ label = '';
440
+ hint = '';
441
+ error = '';
442
+ required = false;
443
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbFormFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
444
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbFormFieldComponent, isStandalone: true, selector: "mb-form-field", inputs: { label: "label", hint: "hint", error: "error", required: "required" }, ngImport: i0, template: "<label class=\"mb-form-field\">\n <span *ngIf=\"label\" class=\"mb-form-field__label\">\n {{ label }} <span *ngIf=\"required\" class=\"mb-form-field__required\">*</span>\n </span>\n <span class=\"mb-form-field__control\"><ng-content></ng-content></span>\n <span *ngIf=\"error || hint\" class=\"mb-form-field__message\" [class.mb-form-field__message--error]=\"error\">\n {{ error || hint }}\n </span>\n</label>\n", styles: [".mb-form-field{display:flex;flex-direction:column;gap:.45rem;inline-size:100%}.mb-form-field__label{color:var(--mb-color-text);font-size:.875rem;font-weight:650}.mb-form-field__required{color:var(--mb-color-danger)}.mb-form-field__control{display:block}.mb-form-field__message{color:var(--mb-color-muted);font-size:.75rem}.mb-form-field__message--error{color:var(--mb-color-danger)}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
445
+ }
446
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbFormFieldComponent, decorators: [{
447
+ type: Component,
448
+ args: [{ selector: 'mb-form-field', standalone: true, imports: [NgIf], changeDetection: ChangeDetectionStrategy.OnPush, template: "<label class=\"mb-form-field\">\n <span *ngIf=\"label\" class=\"mb-form-field__label\">\n {{ label }} <span *ngIf=\"required\" class=\"mb-form-field__required\">*</span>\n </span>\n <span class=\"mb-form-field__control\"><ng-content></ng-content></span>\n <span *ngIf=\"error || hint\" class=\"mb-form-field__message\" [class.mb-form-field__message--error]=\"error\">\n {{ error || hint }}\n </span>\n</label>\n", styles: [".mb-form-field{display:flex;flex-direction:column;gap:.45rem;inline-size:100%}.mb-form-field__label{color:var(--mb-color-text);font-size:.875rem;font-weight:650}.mb-form-field__required{color:var(--mb-color-danger)}.mb-form-field__control{display:block}.mb-form-field__message{color:var(--mb-color-muted);font-size:.75rem}.mb-form-field__message--error{color:var(--mb-color-danger)}\n"] }]
449
+ }], propDecorators: { label: [{
450
+ type: Input
451
+ }], hint: [{
452
+ type: Input
453
+ }], error: [{
454
+ type: Input
455
+ }], required: [{
456
+ type: Input
457
+ }] } });
458
+
459
+ class MbSelectComponent {
460
+ messages = inject(MB_UI_MESSAGES);
461
+ options = [];
462
+ placeholder;
463
+ size = 'md';
464
+ disabled = false;
465
+ invalid = false;
466
+ valueChange = new EventEmitter();
467
+ value = null;
468
+ get effectivePlaceholder() {
469
+ return this.placeholder ?? this.messages.form.selectPlaceholder;
470
+ }
471
+ onChange = () => undefined;
472
+ onTouched = () => undefined;
473
+ writeValue(value) {
474
+ this.value = value;
475
+ }
476
+ registerOnChange(fn) {
477
+ this.onChange = fn;
478
+ }
479
+ registerOnTouched(fn) {
480
+ this.onTouched = fn;
481
+ }
482
+ setDisabledState(isDisabled) {
483
+ this.disabled = isDisabled;
484
+ }
485
+ compareByValue = (a, b) => a === b;
486
+ handleChange(value) {
487
+ this.value = value;
488
+ this.onChange(value);
489
+ this.valueChange.emit(value);
490
+ }
491
+ blur() {
492
+ this.onTouched();
493
+ }
494
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
495
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbSelectComponent, isStandalone: true, selector: "mb-select", inputs: { options: "options", placeholder: "placeholder", size: "size", disabled: "disabled", invalid: "invalid" }, outputs: { valueChange: "valueChange" }, providers: [
496
+ {
497
+ provide: NG_VALUE_ACCESSOR,
498
+ useExisting: forwardRef(() => MbSelectComponent),
499
+ multi: true,
500
+ },
501
+ ], ngImport: i0, template: "<select\n class=\"mb-select\"\n [ngClass]=\"['mb-select--' + size, invalid ? 'mb-select--invalid' : '']\"\n [disabled]=\"disabled\"\n [compareWith]=\"compareByValue\"\n [ngModel]=\"value\"\n (ngModelChange)=\"handleChange($event)\"\n (blur)=\"blur()\">\n <option [ngValue]=\"null\">{{ effectivePlaceholder }}</option>\n <option *ngFor=\"let item of options\" [ngValue]=\"item.value\" [disabled]=\"item.disabled\">\n {{ item.label }}\n </option>\n</select>\n", styles: [".mb-select{inline-size:100%;border:1px solid var(--mb-color-border);border-radius:var(--mb-radius-md);background:var(--mb-color-bg);color:var(--mb-color-text);font:inherit;padding-inline:.875rem 2.25rem;outline:none;cursor:pointer;transition:border-color var(--mb-transition-fast),box-shadow var(--mb-transition-fast)}.mb-select:focus{border-color:var(--mb-color-primary);box-shadow:var(--mb-focus-ring)}.mb-select:disabled{cursor:not-allowed;color:var(--mb-color-disabled)}.mb-select--invalid{border-color:var(--mb-color-danger)}.mb-select--sm{min-height:2rem;font-size:.8125rem}.mb-select--md{min-height:2.5rem;font-size:.875rem}.mb-select--lg{min-height:3rem;font-size:.9375rem}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
502
+ }
503
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbSelectComponent, decorators: [{
504
+ type: Component,
505
+ args: [{ selector: 'mb-select', standalone: true, imports: [FormsModule, NgFor, NgClass], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
506
+ {
507
+ provide: NG_VALUE_ACCESSOR,
508
+ useExisting: forwardRef(() => MbSelectComponent),
509
+ multi: true,
510
+ },
511
+ ], template: "<select\n class=\"mb-select\"\n [ngClass]=\"['mb-select--' + size, invalid ? 'mb-select--invalid' : '']\"\n [disabled]=\"disabled\"\n [compareWith]=\"compareByValue\"\n [ngModel]=\"value\"\n (ngModelChange)=\"handleChange($event)\"\n (blur)=\"blur()\">\n <option [ngValue]=\"null\">{{ effectivePlaceholder }}</option>\n <option *ngFor=\"let item of options\" [ngValue]=\"item.value\" [disabled]=\"item.disabled\">\n {{ item.label }}\n </option>\n</select>\n", styles: [".mb-select{inline-size:100%;border:1px solid var(--mb-color-border);border-radius:var(--mb-radius-md);background:var(--mb-color-bg);color:var(--mb-color-text);font:inherit;padding-inline:.875rem 2.25rem;outline:none;cursor:pointer;transition:border-color var(--mb-transition-fast),box-shadow var(--mb-transition-fast)}.mb-select:focus{border-color:var(--mb-color-primary);box-shadow:var(--mb-focus-ring)}.mb-select:disabled{cursor:not-allowed;color:var(--mb-color-disabled)}.mb-select--invalid{border-color:var(--mb-color-danger)}.mb-select--sm{min-height:2rem;font-size:.8125rem}.mb-select--md{min-height:2.5rem;font-size:.875rem}.mb-select--lg{min-height:3rem;font-size:.9375rem}\n"] }]
512
+ }], propDecorators: { options: [{
513
+ type: Input
514
+ }], placeholder: [{
515
+ type: Input
516
+ }], size: [{
517
+ type: Input
518
+ }], disabled: [{
519
+ type: Input
520
+ }], invalid: [{
521
+ type: Input
522
+ }], valueChange: [{
523
+ type: Output
524
+ }] } });
525
+
526
+ class MbComboboxComponent {
527
+ cdr;
528
+ host;
529
+ messages = inject(MB_UI_MESSAGES);
530
+ data = [];
531
+ textField = 'label';
532
+ valueField = 'value';
533
+ placeholder;
534
+ searchPlaceholder;
535
+ size = 'md';
536
+ disabled = false;
537
+ invalid = false;
538
+ filterable = true;
539
+ clearable = true;
540
+ defaultItem = null;
541
+ defaultItemText;
542
+ /** When true, the model value is the primitive from `valueField` instead of the full data item. */
543
+ valuePrimitive = false;
544
+ valueChange = new EventEmitter();
545
+ openedChange = new EventEmitter();
546
+ hostClass = 'mb-combobox-host';
547
+ value = null;
548
+ opened = false;
549
+ search = '';
550
+ normalizedOptions = [];
551
+ get effectivePlaceholder() {
552
+ return this.placeholder ?? this.messages.form.selectPlaceholder;
553
+ }
554
+ get effectiveSearchPlaceholder() {
555
+ return this.searchPlaceholder ?? this.messages.form.searchPlaceholder;
556
+ }
557
+ get effectiveDefaultItemText() {
558
+ return this.defaultItemText ?? this.messages.form.selectDefaultItem;
559
+ }
560
+ get clearAriaLabel() {
561
+ return this.messages.common.clear;
562
+ }
563
+ onChange = () => undefined;
564
+ onTouched = () => undefined;
565
+ constructor(cdr, host) {
566
+ this.cdr = cdr;
567
+ this.host = host;
568
+ }
569
+ ngOnChanges(changes) {
570
+ if (changes['data'] || changes['textField'] || changes['valueField']) {
571
+ this.normalizedOptions = this.normalizeOptions(this.data);
572
+ this.cdr.markForCheck();
573
+ }
574
+ }
575
+ onDocumentClick(event) {
576
+ if (this.opened && !this.host.nativeElement.contains(event.target)) {
577
+ this.close();
578
+ }
579
+ }
580
+ onDocumentKeydown(event) {
581
+ if (event.key === 'Escape' && this.opened) {
582
+ event.preventDefault();
583
+ this.close();
584
+ }
585
+ }
586
+ writeValue(value) {
587
+ this.value = value ?? null;
588
+ this.cdr.markForCheck();
589
+ }
590
+ registerOnChange(fn) {
591
+ this.onChange = fn;
592
+ }
593
+ registerOnTouched(fn) {
594
+ this.onTouched = fn;
595
+ }
596
+ setDisabledState(isDisabled) {
597
+ this.disabled = isDisabled;
598
+ this.cdr.markForCheck();
599
+ }
600
+ get filteredOptions() {
601
+ if (!this.filterable || !this.search.trim()) {
602
+ return this.normalizedOptions;
603
+ }
604
+ const term = this.search.trim().toLocaleLowerCase();
605
+ return this.normalizedOptions.filter((option) => option.label.toLocaleLowerCase().includes(term));
606
+ }
607
+ get displayLabel() {
608
+ if (this.value === null || this.value === undefined) {
609
+ return '';
610
+ }
611
+ const match = this.normalizedOptions.find((option) => this.compareValues(option.value, this.value));
612
+ return match?.label ?? String(this.value);
613
+ }
614
+ toggle() {
615
+ if (this.disabled)
616
+ return;
617
+ this.opened ? this.close() : this.open();
618
+ }
619
+ open() {
620
+ if (this.disabled)
621
+ return;
622
+ this.opened = true;
623
+ this.search = '';
624
+ this.openedChange.emit(true);
625
+ this.cdr.markForCheck();
626
+ }
627
+ close() {
628
+ this.opened = false;
629
+ this.onTouched();
630
+ this.openedChange.emit(false);
631
+ this.cdr.markForCheck();
632
+ }
633
+ selectOption(option) {
634
+ if (option.disabled)
635
+ return;
636
+ this.commit(option.value);
637
+ this.close();
638
+ }
639
+ selectDefault() {
640
+ this.commit(this.defaultItem);
641
+ this.close();
642
+ }
643
+ clear(event) {
644
+ event?.stopPropagation();
645
+ if (this.disabled)
646
+ return;
647
+ this.commit(null);
648
+ this.close();
649
+ }
650
+ commit(value) {
651
+ this.value = value;
652
+ this.onChange(value);
653
+ this.valueChange.emit(value);
654
+ this.cdr.markForCheck();
655
+ }
656
+ normalizeOptions(source) {
657
+ if (!source?.length)
658
+ return [];
659
+ const first = source[0];
660
+ if (first && typeof first === 'object' && 'label' in first && 'value' in first) {
661
+ return source;
662
+ }
663
+ return source.map((item) => ({
664
+ label: this.readFieldLabel(item, this.textField),
665
+ value: (this.valuePrimitive ? this.readFieldValue(item, this.valueField) : item),
666
+ disabled: Boolean(this.readFieldValue(item, 'disabled')),
667
+ }));
668
+ }
669
+ readFieldLabel(item, field) {
670
+ const value = this.readFieldValue(item, field);
671
+ return value === null || value === undefined ? '' : String(value);
672
+ }
673
+ readFieldValue(item, field) {
674
+ if (item && typeof item === 'object' && field in item) {
675
+ return item[field];
676
+ }
677
+ return item;
678
+ }
679
+ isSelected(option) {
680
+ return this.compareValues(option.value, this.value);
681
+ }
682
+ compareValues(a, b) {
683
+ if (a === b)
684
+ return true;
685
+ if (a === null || b === null)
686
+ return false;
687
+ if (!this.valuePrimitive && typeof a === 'object' && typeof b === 'object') {
688
+ const aRecord = a;
689
+ const bRecord = b;
690
+ if (this.valueField in aRecord || this.valueField in bRecord) {
691
+ return aRecord[this.valueField] === bRecord[this.valueField];
692
+ }
693
+ }
694
+ return false;
695
+ }
696
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbComboboxComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
697
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbComboboxComponent, isStandalone: true, selector: "mb-combobox", inputs: { data: "data", textField: "textField", valueField: "valueField", placeholder: "placeholder", searchPlaceholder: "searchPlaceholder", size: "size", disabled: "disabled", invalid: "invalid", filterable: "filterable", clearable: "clearable", defaultItem: "defaultItem", defaultItemText: "defaultItemText", valuePrimitive: "valuePrimitive" }, outputs: { valueChange: "valueChange", openedChange: "openedChange" }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown": "onDocumentKeydown($event)" }, properties: { "class": "this.hostClass" } }, providers: [
698
+ {
699
+ provide: NG_VALUE_ACCESSOR,
700
+ useExisting: forwardRef(() => MbComboboxComponent),
701
+ multi: true,
702
+ },
703
+ ], usesOnChanges: true, ngImport: i0, template: "<div class=\"mb-combobox\" [class.mb-combobox--open]=\"opened\" [class.mb-combobox--disabled]=\"disabled\">\r\n <button\r\n type=\"button\"\r\n class=\"mb-combobox__trigger\"\r\n [ngClass]=\"['mb-combobox__trigger--' + size, invalid ? 'mb-combobox__trigger--invalid' : '']\"\r\n [disabled]=\"disabled\"\r\n [attr.aria-expanded]=\"opened\"\r\n aria-haspopup=\"listbox\"\r\n (click)=\"toggle()\">\r\n <span class=\"mb-combobox__value\" [class.mb-combobox__value--placeholder]=\"!displayLabel\">\r\n {{ displayLabel || effectivePlaceholder }}\r\n </span>\r\n <span class=\"mb-combobox__actions\">\r\n <button\r\n *ngIf=\"clearable && value !== null && value !== undefined && !disabled\"\r\n type=\"button\"\r\n class=\"mb-combobox__clear\"\r\n [attr.aria-label]=\"clearAriaLabel\"\r\n (click)=\"clear($event)\">\u00D7</button>\r\n <span class=\"mb-combobox__chevron\" [class.mb-combobox__chevron--open]=\"opened\">\u25BE</span>\r\n </span>\r\n </button>\r\n\r\n <div *ngIf=\"opened\" class=\"mb-combobox__panel\" role=\"listbox\">\r\n <input\r\n *ngIf=\"filterable\"\r\n class=\"mb-combobox__search\"\r\n type=\"text\"\r\n [placeholder]=\"effectiveSearchPlaceholder\"\r\n [ngModel]=\"search\"\r\n (ngModelChange)=\"search = $event\"\r\n (click)=\"$event.stopPropagation()\" />\r\n\r\n <button\r\n *ngIf=\"defaultItem !== undefined\"\r\n type=\"button\"\r\n class=\"mb-combobox__option\"\r\n (click)=\"selectDefault()\">\r\n {{ effectiveDefaultItemText }}\r\n </button>\r\n\r\n <button\r\n *ngFor=\"let option of filteredOptions\"\r\n type=\"button\"\r\n class=\"mb-combobox__option\"\r\n [class.mb-combobox__option--selected]=\"isSelected(option)\"\r\n [disabled]=\"option.disabled\"\r\n (click)=\"selectOption(option)\">\r\n {{ option.label }}\r\n </button>\r\n\r\n <div *ngIf=\"!filteredOptions.length\" class=\"mb-combobox__empty\">{{ messages.form.noMatches }}</div>\r\n </div>\r\n</div>\r\n", styles: [".mb-combobox-host{display:block;width:100%}.mb-combobox{position:relative;width:100%}.mb-combobox__trigger{width:100%;min-height:40px;display:flex;align-items:center;justify-content:space-between;gap:8px;border:1px solid var(--mb-border, #e2e8f0);border-radius:var(--mb-radius-md, 10px);background:var(--mb-input-bg, #fff);color:var(--mb-text, #0f172a);padding:0 12px;cursor:pointer;font:inherit}.mb-combobox__trigger--sm{min-height:34px}.mb-combobox__trigger--lg{min-height:46px}.mb-combobox__trigger--invalid{border-color:var(--mb-danger, #dc2626)}.mb-combobox--disabled .mb-combobox__trigger{opacity:.6;cursor:not-allowed}.mb-combobox__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mb-combobox__value--placeholder{color:var(--mb-muted, #64748b)}.mb-combobox__actions{display:inline-flex;align-items:center;gap:4px;flex-shrink:0}.mb-combobox__clear{border:none;background:transparent;color:var(--mb-muted, #64748b);cursor:pointer;font-size:18px;line-height:1}.mb-combobox__chevron{transition:transform .15s ease}.mb-combobox__chevron--open{transform:rotate(180deg)}.mb-combobox__panel{position:absolute;inset-block-start:calc(100% + 4px);inset-inline:0;z-index:40;max-height:280px;overflow:auto;border:1px solid var(--mb-border, #e2e8f0);border-radius:var(--mb-radius-md, 10px);background:var(--mb-surface, #fff);box-shadow:var(--mb-shadow-md, 0 10px 24px rgba(15, 23, 42, .12));padding:6px}.mb-combobox__search{width:100%;min-height:34px;margin-bottom:6px;border:1px solid var(--mb-border, #e2e8f0);border-radius:var(--mb-radius-sm, 8px);padding:0 10px;font:inherit}.mb-combobox__option{width:100%;text-align:start;border:none;background:transparent;border-radius:var(--mb-radius-sm, 8px);padding:10px 12px;cursor:pointer;font:inherit}.mb-combobox__option:hover:not(:disabled),.mb-combobox__option--selected{background:color-mix(in srgb,var(--mb-primary, #2563eb) 10%,#fff);color:var(--mb-primary, #2563eb)}.mb-combobox__option:disabled{opacity:.45;cursor:not-allowed}.mb-combobox__empty{padding:12px;color:var(--mb-muted, #64748b);text-align:center;font-size:13px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
704
+ }
705
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbComboboxComponent, decorators: [{
706
+ type: Component,
707
+ args: [{ selector: 'mb-combobox', standalone: true, imports: [FormsModule, NgClass, NgFor, NgIf], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
708
+ {
709
+ provide: NG_VALUE_ACCESSOR,
710
+ useExisting: forwardRef(() => MbComboboxComponent),
711
+ multi: true,
712
+ },
713
+ ], template: "<div class=\"mb-combobox\" [class.mb-combobox--open]=\"opened\" [class.mb-combobox--disabled]=\"disabled\">\r\n <button\r\n type=\"button\"\r\n class=\"mb-combobox__trigger\"\r\n [ngClass]=\"['mb-combobox__trigger--' + size, invalid ? 'mb-combobox__trigger--invalid' : '']\"\r\n [disabled]=\"disabled\"\r\n [attr.aria-expanded]=\"opened\"\r\n aria-haspopup=\"listbox\"\r\n (click)=\"toggle()\">\r\n <span class=\"mb-combobox__value\" [class.mb-combobox__value--placeholder]=\"!displayLabel\">\r\n {{ displayLabel || effectivePlaceholder }}\r\n </span>\r\n <span class=\"mb-combobox__actions\">\r\n <button\r\n *ngIf=\"clearable && value !== null && value !== undefined && !disabled\"\r\n type=\"button\"\r\n class=\"mb-combobox__clear\"\r\n [attr.aria-label]=\"clearAriaLabel\"\r\n (click)=\"clear($event)\">\u00D7</button>\r\n <span class=\"mb-combobox__chevron\" [class.mb-combobox__chevron--open]=\"opened\">\u25BE</span>\r\n </span>\r\n </button>\r\n\r\n <div *ngIf=\"opened\" class=\"mb-combobox__panel\" role=\"listbox\">\r\n <input\r\n *ngIf=\"filterable\"\r\n class=\"mb-combobox__search\"\r\n type=\"text\"\r\n [placeholder]=\"effectiveSearchPlaceholder\"\r\n [ngModel]=\"search\"\r\n (ngModelChange)=\"search = $event\"\r\n (click)=\"$event.stopPropagation()\" />\r\n\r\n <button\r\n *ngIf=\"defaultItem !== undefined\"\r\n type=\"button\"\r\n class=\"mb-combobox__option\"\r\n (click)=\"selectDefault()\">\r\n {{ effectiveDefaultItemText }}\r\n </button>\r\n\r\n <button\r\n *ngFor=\"let option of filteredOptions\"\r\n type=\"button\"\r\n class=\"mb-combobox__option\"\r\n [class.mb-combobox__option--selected]=\"isSelected(option)\"\r\n [disabled]=\"option.disabled\"\r\n (click)=\"selectOption(option)\">\r\n {{ option.label }}\r\n </button>\r\n\r\n <div *ngIf=\"!filteredOptions.length\" class=\"mb-combobox__empty\">{{ messages.form.noMatches }}</div>\r\n </div>\r\n</div>\r\n", styles: [".mb-combobox-host{display:block;width:100%}.mb-combobox{position:relative;width:100%}.mb-combobox__trigger{width:100%;min-height:40px;display:flex;align-items:center;justify-content:space-between;gap:8px;border:1px solid var(--mb-border, #e2e8f0);border-radius:var(--mb-radius-md, 10px);background:var(--mb-input-bg, #fff);color:var(--mb-text, #0f172a);padding:0 12px;cursor:pointer;font:inherit}.mb-combobox__trigger--sm{min-height:34px}.mb-combobox__trigger--lg{min-height:46px}.mb-combobox__trigger--invalid{border-color:var(--mb-danger, #dc2626)}.mb-combobox--disabled .mb-combobox__trigger{opacity:.6;cursor:not-allowed}.mb-combobox__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mb-combobox__value--placeholder{color:var(--mb-muted, #64748b)}.mb-combobox__actions{display:inline-flex;align-items:center;gap:4px;flex-shrink:0}.mb-combobox__clear{border:none;background:transparent;color:var(--mb-muted, #64748b);cursor:pointer;font-size:18px;line-height:1}.mb-combobox__chevron{transition:transform .15s ease}.mb-combobox__chevron--open{transform:rotate(180deg)}.mb-combobox__panel{position:absolute;inset-block-start:calc(100% + 4px);inset-inline:0;z-index:40;max-height:280px;overflow:auto;border:1px solid var(--mb-border, #e2e8f0);border-radius:var(--mb-radius-md, 10px);background:var(--mb-surface, #fff);box-shadow:var(--mb-shadow-md, 0 10px 24px rgba(15, 23, 42, .12));padding:6px}.mb-combobox__search{width:100%;min-height:34px;margin-bottom:6px;border:1px solid var(--mb-border, #e2e8f0);border-radius:var(--mb-radius-sm, 8px);padding:0 10px;font:inherit}.mb-combobox__option{width:100%;text-align:start;border:none;background:transparent;border-radius:var(--mb-radius-sm, 8px);padding:10px 12px;cursor:pointer;font:inherit}.mb-combobox__option:hover:not(:disabled),.mb-combobox__option--selected{background:color-mix(in srgb,var(--mb-primary, #2563eb) 10%,#fff);color:var(--mb-primary, #2563eb)}.mb-combobox__option:disabled{opacity:.45;cursor:not-allowed}.mb-combobox__empty{padding:12px;color:var(--mb-muted, #64748b);text-align:center;font-size:13px}\n"] }]
714
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }], propDecorators: { data: [{
715
+ type: Input
716
+ }], textField: [{
717
+ type: Input
718
+ }], valueField: [{
719
+ type: Input
720
+ }], placeholder: [{
721
+ type: Input
722
+ }], searchPlaceholder: [{
723
+ type: Input
724
+ }], size: [{
725
+ type: Input
726
+ }], disabled: [{
727
+ type: Input
728
+ }], invalid: [{
729
+ type: Input
730
+ }], filterable: [{
731
+ type: Input
732
+ }], clearable: [{
733
+ type: Input
734
+ }], defaultItem: [{
735
+ type: Input
736
+ }], defaultItemText: [{
737
+ type: Input
738
+ }], valuePrimitive: [{
739
+ type: Input
740
+ }], valueChange: [{
741
+ type: Output
742
+ }], openedChange: [{
743
+ type: Output
744
+ }], hostClass: [{
745
+ type: HostBinding,
746
+ args: ['class']
747
+ }], onDocumentClick: [{
748
+ type: HostListener,
749
+ args: ['document:click', ['$event']]
750
+ }], onDocumentKeydown: [{
751
+ type: HostListener,
752
+ args: ['document:keydown', ['$event']]
753
+ }] } });
754
+
755
+ function mbGridDefaultState(take = 10) {
756
+ return { skip: 0, take, sort: [], group: [] };
757
+ }
758
+ function mbGridGetValue(row, field) {
759
+ if (!row || !field)
760
+ return undefined;
761
+ const path = String(field).split('.');
762
+ let cursor = row;
763
+ for (const segment of path) {
764
+ if (cursor == null)
765
+ return undefined;
766
+ cursor = cursor[segment];
767
+ }
768
+ return cursor;
769
+ }
770
+ function mbGridNormalize(value, ignoreCase = true) {
771
+ const text = value === null || value === undefined ? '' : String(value);
772
+ return ignoreCase ? text.trim().toLocaleLowerCase() : text.trim();
773
+ }
774
+ function mbGridApplyFilter(rows, filter, columns = []) {
775
+ if (!filter || !filter.filters?.length)
776
+ return rows;
777
+ return rows.filter((row) => mbGridEvaluateComposite(row, filter, columns));
778
+ }
779
+ function mbGridApplySort(rows, sort = [], columns = []) {
780
+ if (!sort.length)
781
+ return rows;
782
+ return [...rows].sort((a, b) => {
783
+ for (const descriptor of sort) {
784
+ const column = columns.find((item) => String(item.field) === String(descriptor.field));
785
+ const result = column?.compare
786
+ ? column.compare(a, b, descriptor.dir)
787
+ : mbGridCompareValues(column?.valueGetter ? column.valueGetter(a, -1) : mbGridGetValue(a, descriptor.field), column?.valueGetter ? column.valueGetter(b, -1) : mbGridGetValue(b, descriptor.field), descriptor.dir);
788
+ if (result !== 0)
789
+ return result;
790
+ }
791
+ return 0;
792
+ });
793
+ }
794
+ function mbGridProcess(rows, state, columns = [], expandedGroupKeys = new Set()) {
795
+ const filtered = mbGridApplyFilter(rows, state.filter, columns);
796
+ const sorted = mbGridApplySort(filtered, state.sort, columns);
797
+ const total = sorted.length;
798
+ const aggregates = mbGridAggregateRows(sorted, columns);
799
+ if (state.group?.length) {
800
+ const grouped = mbGridBuildGroupRows(sorted, state.group, columns, expandedGroupKeys);
801
+ return { data: grouped, total, aggregates };
802
+ }
803
+ const paged = sorted.slice(state.skip, state.skip + state.take);
804
+ return {
805
+ data: paged.map((dataItem, rowIndex) => ({ kind: 'data', dataItem, rowIndex: state.skip + rowIndex })),
806
+ total,
807
+ aggregates,
808
+ };
809
+ }
810
+ function mbGridFormatValue(row, column, rowIndex, localeOrOptions = 'en-US') {
811
+ const options = typeof localeOrOptions === 'string'
812
+ ? { locale: localeOrOptions }
813
+ : localeOrOptions;
814
+ const locale = options.locale ?? 'en-US';
815
+ const value = column.valueGetter ? column.valueGetter(row, rowIndex) : mbGridGetValue(row, column.field);
816
+ if (column.formatter)
817
+ return column.formatter(value, row, rowIndex);
818
+ if (value === null || value === undefined || value === '')
819
+ return '—';
820
+ if (column.type === 'boolean')
821
+ return value ? (options.yes ?? 'Yes') : (options.no ?? 'No');
822
+ if (column.type === 'enum' && column.enumMap)
823
+ return column.enumMap[value] ?? String(value);
824
+ if (column.type === 'number' && typeof value === 'number')
825
+ return new Intl.NumberFormat(locale).format(value);
826
+ if (column.type === 'date') {
827
+ const date = value instanceof Date ? value : new Date(String(value));
828
+ return Number.isNaN(date.getTime()) ? String(value) : new Intl.DateTimeFormat(locale).format(date);
829
+ }
830
+ return String(value);
831
+ }
832
+ function mbGridBuildFilter(filtersByField) {
833
+ const filters = Object.entries(filtersByField)
834
+ .filter(([, item]) => item.operator === 'isNull' || item.operator === 'isNotNull' || item.operator === 'isEmpty' || item.operator === 'isNotEmpty' || item.value !== null && item.value !== undefined && String(item.value).trim() !== '')
835
+ .map(([field, item]) => ({ field, operator: item.operator, value: item.value, ignoreCase: true }));
836
+ return filters.length ? { logic: 'and', filters } : undefined;
837
+ }
838
+ function mbGridToODataQuery(state) {
839
+ const params = [`$skip=${state.skip}`, `$top=${state.take}`];
840
+ if (state.sort?.length) {
841
+ params.push(`$orderby=${state.sort.map((s) => `${String(s.field)} ${s.dir}`).join(',')}`);
842
+ }
843
+ const filter = mbGridFilterToOData(state.filter);
844
+ if (filter)
845
+ params.push(`$filter=${encodeURIComponent(filter)}`);
846
+ return params.join('&');
847
+ }
848
+ function mbGridToLaravelQuery(state) {
849
+ return {
850
+ page: Math.floor(state.skip / state.take) + 1,
851
+ per_page: state.take,
852
+ sort: state.sort.map((s) => `${s.dir === 'desc' ? '-' : ''}${String(s.field)}`).join(','),
853
+ filter: state.filter,
854
+ group: state.group.map((g) => String(g.field)),
855
+ };
856
+ }
857
+ function mbGridExportCsv(rows, columns, fileName = 'grid-export.csv', localeOrOptions = 'en-US') {
858
+ const visibleColumns = columns.filter((column) => !column.hidden && column.exportable !== false);
859
+ const escape = (value) => `"${String(value ?? '').replace(/"/g, '""')}"`;
860
+ const header = visibleColumns.map((column) => escape(column.title)).join(',');
861
+ const body = rows
862
+ .map((row, rowIndex) => visibleColumns.map((column) => escape(mbGridFormatValue(row, column, rowIndex, localeOrOptions))).join(','))
863
+ .join('\n');
864
+ const blob = new Blob(['\ufeff', header, '\n', body], { type: 'text/csv;charset=utf-8;' });
865
+ const url = URL.createObjectURL(blob);
866
+ const anchor = document.createElement('a');
867
+ anchor.href = url;
868
+ anchor.download = fileName;
869
+ anchor.click();
870
+ URL.revokeObjectURL(url);
871
+ }
872
+ function mbGridEvaluateComposite(row, filter, columns) {
873
+ const results = filter.filters.map((item) => {
874
+ if ('logic' in item)
875
+ return mbGridEvaluateComposite(row, item, columns);
876
+ const column = columns.find((candidate) => String(candidate.field) === String(item.field));
877
+ if (column?.filterPredicate)
878
+ return column.filterPredicate(row, item);
879
+ const value = column?.valueGetter ? column.valueGetter(row, -1) : mbGridGetValue(row, item.field);
880
+ return mbGridEvaluateFilter(value, item.operator, item.value, item.ignoreCase !== false);
881
+ });
882
+ return filter.logic === 'or' ? results.some(Boolean) : results.every(Boolean);
883
+ }
884
+ function mbGridEvaluateFilter(value, operator, expected, ignoreCase = true) {
885
+ const actualText = mbGridNormalize(value, ignoreCase);
886
+ const expectedText = mbGridNormalize(expected, ignoreCase);
887
+ const actualNumber = typeof value === 'number' ? value : Number(value);
888
+ const expectedNumber = Number(expected);
889
+ switch (operator) {
890
+ case 'contains': return actualText.includes(expectedText);
891
+ case 'doesNotContain': return !actualText.includes(expectedText);
892
+ case 'startsWith': return actualText.startsWith(expectedText);
893
+ case 'endsWith': return actualText.endsWith(expectedText);
894
+ case 'eq': return actualText === expectedText;
895
+ case 'neq': return actualText !== expectedText;
896
+ case 'gt': return !Number.isNaN(actualNumber) && actualNumber > expectedNumber;
897
+ case 'gte': return !Number.isNaN(actualNumber) && actualNumber >= expectedNumber;
898
+ case 'lt': return !Number.isNaN(actualNumber) && actualNumber < expectedNumber;
899
+ case 'lte': return !Number.isNaN(actualNumber) && actualNumber <= expectedNumber;
900
+ case 'between': {
901
+ const range = Array.isArray(expected) ? expected : String(expected ?? '').split(',');
902
+ const min = Number(range[0]);
903
+ const max = Number(range[1]);
904
+ return !Number.isNaN(actualNumber) && !Number.isNaN(min) && !Number.isNaN(max) && actualNumber >= min && actualNumber <= max;
905
+ }
906
+ case 'isNull': return value === null || value === undefined;
907
+ case 'isNotNull': return value !== null && value !== undefined;
908
+ case 'isEmpty': return actualText.length === 0;
909
+ case 'isNotEmpty': return actualText.length > 0;
910
+ default: return true;
911
+ }
912
+ }
913
+ function mbGridCompareValues(first, second, direction) {
914
+ const modifier = direction === 'asc' ? 1 : -1;
915
+ if (first == null && second == null)
916
+ return 0;
917
+ if (first == null)
918
+ return -1 * modifier;
919
+ if (second == null)
920
+ return 1 * modifier;
921
+ if (first instanceof Date || second instanceof Date) {
922
+ return ((new Date(first).getTime() || 0) - (new Date(second).getTime() || 0)) * modifier;
923
+ }
924
+ if (typeof first === 'number' || typeof second === 'number') {
925
+ return (Number(first) - Number(second)) * modifier;
926
+ }
927
+ return mbGridNormalize(first).localeCompare(mbGridNormalize(second), undefined, { numeric: true }) * modifier;
928
+ }
929
+ function mbGridBuildGroupRows(rows, groups, columns, expandedGroupKeys, level = 0, parentKey = '') {
930
+ if (!groups.length) {
931
+ return rows.map((dataItem, rowIndex) => ({ kind: 'data', dataItem, rowIndex }));
932
+ }
933
+ const [descriptor, ...rest] = groups;
934
+ const buckets = new Map();
935
+ for (const row of rows) {
936
+ const raw = mbGridGetValue(row, descriptor.field);
937
+ const key = String(raw ?? '');
938
+ buckets.set(key, [...(buckets.get(key) ?? []), row]);
939
+ }
940
+ const sortedKeys = [...buckets.keys()].sort((a, b) => descriptor.dir === 'desc' ? b.localeCompare(a, undefined, { numeric: true }) : a.localeCompare(b, undefined, { numeric: true }));
941
+ const result = [];
942
+ for (const key of sortedKeys) {
943
+ const groupRows = buckets.get(key) ?? [];
944
+ const groupKey = `${parentKey}${String(descriptor.field)}:${key}|`;
945
+ const expanded = expandedGroupKeys.has(groupKey) || expandedGroupKeys.size === 0;
946
+ const groupRow = {
947
+ kind: 'group',
948
+ field: descriptor.field,
949
+ value: key || '—',
950
+ level,
951
+ count: groupRows.length,
952
+ expanded,
953
+ key: groupKey,
954
+ aggregates: mbGridAggregateRows(groupRows, columns),
955
+ };
956
+ result.push(groupRow);
957
+ if (expanded) {
958
+ result.push(...mbGridBuildGroupRows(groupRows, rest, columns, expandedGroupKeys, level + 1, groupKey));
959
+ }
960
+ }
961
+ return result;
962
+ }
963
+ function mbGridAggregateRows(rows, columns) {
964
+ const result = {};
965
+ for (const column of columns) {
966
+ const aggregates = Array.isArray(column.aggregate) ? column.aggregate : column.aggregate ? [column.aggregate] : [];
967
+ if (!aggregates.length)
968
+ continue;
969
+ const values = rows.map((row, index) => column.valueGetter ? column.valueGetter(row, index) : mbGridGetValue(row, column.field)).filter((value) => value !== null && value !== undefined && value !== '');
970
+ const numbers = values.map(Number).filter((value) => !Number.isNaN(value));
971
+ const field = String(column.field);
972
+ result[field] = {};
973
+ for (const aggregate of aggregates) {
974
+ result[field][aggregate] = mbGridAggregateValue(aggregate, numbers, values.length);
975
+ }
976
+ }
977
+ return result;
978
+ }
979
+ function mbGridAggregateValue(aggregate, numbers, count) {
980
+ switch (aggregate) {
981
+ case 'count': return count;
982
+ case 'sum': return numbers.reduce((sum, value) => sum + value, 0);
983
+ case 'avg': return numbers.length ? numbers.reduce((sum, value) => sum + value, 0) / numbers.length : 0;
984
+ case 'min': return numbers.length ? Math.min(...numbers) : 0;
985
+ case 'max': return numbers.length ? Math.max(...numbers) : 0;
986
+ default: return 0;
987
+ }
988
+ }
989
+ function mbGridFilterToOData(filter) {
990
+ if (!filter || !filter.filters.length)
991
+ return '';
992
+ const parts = filter.filters
993
+ .map((item) => {
994
+ if ('logic' in item)
995
+ return `(${mbGridFilterToOData(item)})`;
996
+ const field = String(item.field);
997
+ const value = typeof item.value === 'number' ? item.value : `'${String(item.value ?? '').replace(/'/g, "''")}'`;
998
+ switch (item.operator) {
999
+ case 'contains': return `contains(${field},${value})`;
1000
+ case 'doesNotContain': return `not contains(${field},${value})`;
1001
+ case 'startsWith': return `startswith(${field},${value})`;
1002
+ case 'endsWith': return `endswith(${field},${value})`;
1003
+ case 'eq': return `${field} eq ${value}`;
1004
+ case 'neq': return `${field} ne ${value}`;
1005
+ case 'gt': return `${field} gt ${value}`;
1006
+ case 'gte': return `${field} ge ${value}`;
1007
+ case 'lt': return `${field} lt ${value}`;
1008
+ case 'lte': return `${field} le ${value}`;
1009
+ case 'between': return `${field} ge ${Array.isArray(item.value) ? item.value[0] : item.value} and ${field} le ${item.valueTo ?? (Array.isArray(item.value) ? item.value[1] : item.value)}`;
1010
+ case 'isNull': return `${field} eq null`;
1011
+ case 'isNotNull': return `${field} ne null`;
1012
+ case 'isEmpty': return `${field} eq ''`;
1013
+ case 'isNotEmpty': return `${field} ne ''`;
1014
+ default: return '';
1015
+ }
1016
+ })
1017
+ .filter(Boolean);
1018
+ return parts.join(` ${filter.logic} `);
1019
+ }
1020
+
1021
+ function mbGridDownloadBlob(blob, fileName) {
1022
+ const url = URL.createObjectURL(blob);
1023
+ const anchor = document.createElement('a');
1024
+ anchor.href = url;
1025
+ anchor.download = fileName;
1026
+ anchor.click();
1027
+ URL.revokeObjectURL(url);
1028
+ }
1029
+ function mbGridExportJson(rows, columns, fileName = 'grid-export.json') {
1030
+ const visible = columns.filter((column) => !column.hidden && column.exportable !== false);
1031
+ const data = rows.map((row, rowIndex) => {
1032
+ const item = {};
1033
+ for (const column of visible)
1034
+ item[String(column.field)] = column.valueGetter ? column.valueGetter(row, rowIndex) : mbGridGetValue(row, column.field);
1035
+ return item;
1036
+ });
1037
+ mbGridDownloadBlob(new Blob([JSON.stringify(data, null, 2)], { type: 'application/json;charset=utf-8' }), fileName);
1038
+ }
1039
+ /**
1040
+ * Excel-compatible HTML export. It opens directly in Excel/LibreOffice and keeps Unicode/RTL safely.
1041
+ * For true XLSX, wrap this API with SheetJS/xlsx in the host application to avoid forcing a heavy dependency into mb-ui core.
1042
+ */
1043
+ function mbGridExportExcelHtml(rows, columns, fileName = 'grid-export.xls', localeOrOptions = 'en-US', dir = 'ltr') {
1044
+ const visible = columns.filter((column) => !column.hidden && column.exportable !== false);
1045
+ const escape = (value) => String(value ?? '')
1046
+ .replace(/&/g, '&amp;')
1047
+ .replace(/</g, '&lt;')
1048
+ .replace(/>/g, '&gt;')
1049
+ .replace(/"/g, '&quot;');
1050
+ const header = `<tr>${visible.map((column) => `<th>${escape(column.title)}</th>`).join('')}</tr>`;
1051
+ const body = rows.map((row, rowIndex) => `<tr>${visible.map((column) => `<td>${escape(mbGridFormatValue(row, column, rowIndex, localeOrOptions))}</td>`).join('')}</tr>`).join('');
1052
+ const html = `<!doctype html><html><head><meta charset="utf-8"></head><body><table dir="${dir}">${header}${body}</table></body></html>`;
1053
+ mbGridDownloadBlob(new Blob(['\ufeff', html], { type: 'application/vnd.ms-excel;charset=utf-8' }), fileName);
1054
+ }
1055
+
1056
+ class MbGridCellTemplateDirective {
1057
+ templateRef;
1058
+ field = '';
1059
+ constructor(templateRef) {
1060
+ this.templateRef = templateRef;
1061
+ }
1062
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbGridCellTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
1063
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: MbGridCellTemplateDirective, isStandalone: true, selector: "ng-template[mbGridCell]", inputs: { field: ["mbGridCell", "field"] }, ngImport: i0 });
1064
+ }
1065
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbGridCellTemplateDirective, decorators: [{
1066
+ type: Directive,
1067
+ args: [{
1068
+ selector: 'ng-template[mbGridCell]',
1069
+ standalone: true,
1070
+ }]
1071
+ }], ctorParameters: () => [{ type: i0.TemplateRef }], propDecorators: { field: [{
1072
+ type: Input,
1073
+ args: ['mbGridCell']
1074
+ }] } });
1075
+ class MbGridEditTemplateDirective {
1076
+ templateRef;
1077
+ field = '';
1078
+ constructor(templateRef) {
1079
+ this.templateRef = templateRef;
1080
+ }
1081
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbGridEditTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
1082
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: MbGridEditTemplateDirective, isStandalone: true, selector: "ng-template[mbGridEdit]", inputs: { field: ["mbGridEdit", "field"] }, ngImport: i0 });
1083
+ }
1084
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbGridEditTemplateDirective, decorators: [{
1085
+ type: Directive,
1086
+ args: [{
1087
+ selector: 'ng-template[mbGridEdit]',
1088
+ standalone: true,
1089
+ }]
1090
+ }], ctorParameters: () => [{ type: i0.TemplateRef }], propDecorators: { field: [{
1091
+ type: Input,
1092
+ args: ['mbGridEdit']
1093
+ }] } });
1094
+ class MbGridHeaderTemplateDirective {
1095
+ templateRef;
1096
+ field = '';
1097
+ constructor(templateRef) {
1098
+ this.templateRef = templateRef;
1099
+ }
1100
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbGridHeaderTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
1101
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: MbGridHeaderTemplateDirective, isStandalone: true, selector: "ng-template[mbGridHeader]", inputs: { field: ["mbGridHeader", "field"] }, ngImport: i0 });
1102
+ }
1103
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbGridHeaderTemplateDirective, decorators: [{
1104
+ type: Directive,
1105
+ args: [{
1106
+ selector: 'ng-template[mbGridHeader]',
1107
+ standalone: true,
1108
+ }]
1109
+ }], ctorParameters: () => [{ type: i0.TemplateRef }], propDecorators: { field: [{
1110
+ type: Input,
1111
+ args: ['mbGridHeader']
1112
+ }] } });
1113
+ class MbGridFooterTemplateDirective {
1114
+ templateRef;
1115
+ field = '';
1116
+ constructor(templateRef) {
1117
+ this.templateRef = templateRef;
1118
+ }
1119
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbGridFooterTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
1120
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: MbGridFooterTemplateDirective, isStandalone: true, selector: "ng-template[mbGridFooter]", inputs: { field: ["mbGridFooter", "field"] }, ngImport: i0 });
1121
+ }
1122
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbGridFooterTemplateDirective, decorators: [{
1123
+ type: Directive,
1124
+ args: [{
1125
+ selector: 'ng-template[mbGridFooter]',
1126
+ standalone: true,
1127
+ }]
1128
+ }], ctorParameters: () => [{ type: i0.TemplateRef }], propDecorators: { field: [{
1129
+ type: Input,
1130
+ args: ['mbGridFooter']
1131
+ }] } });
1132
+ class MbGridToolbarTemplateDirective {
1133
+ templateRef;
1134
+ constructor(templateRef) {
1135
+ this.templateRef = templateRef;
1136
+ }
1137
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbGridToolbarTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
1138
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: MbGridToolbarTemplateDirective, isStandalone: true, selector: "ng-template[mbGridToolbar]", ngImport: i0 });
1139
+ }
1140
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbGridToolbarTemplateDirective, decorators: [{
1141
+ type: Directive,
1142
+ args: [{
1143
+ selector: 'ng-template[mbGridToolbar]',
1144
+ standalone: true,
1145
+ }]
1146
+ }], ctorParameters: () => [{ type: i0.TemplateRef }] });
1147
+ class MbGridDetailTemplateDirective {
1148
+ templateRef;
1149
+ constructor(templateRef) {
1150
+ this.templateRef = templateRef;
1151
+ }
1152
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbGridDetailTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
1153
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: MbGridDetailTemplateDirective, isStandalone: true, selector: "ng-template[mbGridDetail]", ngImport: i0 });
1154
+ }
1155
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbGridDetailTemplateDirective, decorators: [{
1156
+ type: Directive,
1157
+ args: [{
1158
+ selector: 'ng-template[mbGridDetail]',
1159
+ standalone: true,
1160
+ }]
1161
+ }], ctorParameters: () => [{ type: i0.TemplateRef }] });
1162
+
1163
+ class MbGridStateService {
1164
+ save(key, state, columns, selectedKeys = []) {
1165
+ if (!key || typeof localStorage === 'undefined')
1166
+ return;
1167
+ const payload = {
1168
+ state,
1169
+ selectedKeys,
1170
+ columns: columns.map((column) => ({
1171
+ field: column.field,
1172
+ width: column.width,
1173
+ hidden: column.hidden,
1174
+ locked: column.locked,
1175
+ sticky: column.sticky,
1176
+ })),
1177
+ updatedAt: new Date().toISOString(),
1178
+ };
1179
+ localStorage.setItem(this.storageKey(key), JSON.stringify(payload));
1180
+ }
1181
+ load(key) {
1182
+ if (!key || typeof localStorage === 'undefined')
1183
+ return null;
1184
+ const raw = localStorage.getItem(this.storageKey(key));
1185
+ if (!raw)
1186
+ return null;
1187
+ try {
1188
+ return JSON.parse(raw);
1189
+ }
1190
+ catch {
1191
+ return null;
1192
+ }
1193
+ }
1194
+ clear(key) {
1195
+ if (!key || typeof localStorage === 'undefined')
1196
+ return;
1197
+ localStorage.removeItem(this.storageKey(key));
1198
+ }
1199
+ storageKey(key) { return `mb-ui:grid:${key}`; }
1200
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbGridStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1201
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbGridStateService, providedIn: 'root' });
1202
+ }
1203
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbGridStateService, decorators: [{
1204
+ type: Injectable,
1205
+ args: [{ providedIn: 'root' }]
1206
+ }] });
1207
+
1208
+ class MbGridComponent {
1209
+ cdr;
1210
+ stateStore;
1211
+ uiConfig;
1212
+ messages;
1213
+ data = [];
1214
+ columns = [];
1215
+ loading = false;
1216
+ dataMode = 'client';
1217
+ state = mbGridDefaultState();
1218
+ total = 0;
1219
+ height = null;
1220
+ rowHeight = 44;
1221
+ density = 'comfortable';
1222
+ locale;
1223
+ dir;
1224
+ noRecordsText;
1225
+ loadingText;
1226
+ pageable = true;
1227
+ pageSizeOptions = [10, 20, 50, 100];
1228
+ sortable = true;
1229
+ multiSort = false;
1230
+ filterable = true;
1231
+ filterMode = 'row';
1232
+ groupable = false;
1233
+ selectable = false;
1234
+ selectionMode = 'multiple';
1235
+ rowKey = 'id';
1236
+ selectedKeys = [];
1237
+ resizable = true;
1238
+ reorderable = true;
1239
+ columnMenu = true;
1240
+ showToolbar = true;
1241
+ showSearch = true;
1242
+ showColumnChooser = true;
1243
+ showExport = true;
1244
+ showFooter = false;
1245
+ virtualScroll = false;
1246
+ exportFileName = 'mb-grid-export.csv';
1247
+ excelFileName = 'mb-grid-export.xls';
1248
+ jsonFileName = 'mb-grid-export.json';
1249
+ showExcelExport = true;
1250
+ showJsonExport = true;
1251
+ persistKey = '';
1252
+ autoPersist = false;
1253
+ editable = false;
1254
+ editMode = 'inline';
1255
+ allowAdd = false;
1256
+ allowDelete = false;
1257
+ confirmDelete = false;
1258
+ commandColumnTitle;
1259
+ newItemFactory;
1260
+ expandable = false;
1261
+ detailInitiallyExpanded = false;
1262
+ rowClassName;
1263
+ responsiveColumns = true;
1264
+ stateChange = new EventEmitter();
1265
+ pageChange = new EventEmitter();
1266
+ sortChange = new EventEmitter();
1267
+ filterChange = new EventEmitter();
1268
+ groupChange = new EventEmitter();
1269
+ selectedKeysChange = new EventEmitter();
1270
+ columnResize = new EventEmitter();
1271
+ columnReorder = new EventEmitter();
1272
+ columnVisibilityChange = new EventEmitter();
1273
+ columnLockChange = new EventEmitter();
1274
+ selectionChange = new EventEmitter();
1275
+ edit = new EventEmitter();
1276
+ save = new EventEmitter();
1277
+ cancel = new EventEmitter();
1278
+ remove = new EventEmitter();
1279
+ detailToggle = new EventEmitter();
1280
+ rowClick = new EventEmitter();
1281
+ cellClick = new EventEmitter();
1282
+ cellTemplates;
1283
+ headerTemplates;
1284
+ editTemplates;
1285
+ footerTemplates;
1286
+ toolbarTemplate;
1287
+ detailTemplate;
1288
+ processed = { data: [], total: 0, aggregates: {} };
1289
+ internalColumns = [];
1290
+ globalSearch = '';
1291
+ filtersByField = {};
1292
+ openColumnMenuField = '';
1293
+ resizing;
1294
+ expandedGroupKeys = new Set();
1295
+ expandedDetailKeys = new Set();
1296
+ editingKey = null;
1297
+ editingModel = null;
1298
+ originalModel = null;
1299
+ editingRowIndex = -1;
1300
+ editingIsNew = false;
1301
+ validationErrors = {};
1302
+ mediaQueryLists = new Map();
1303
+ mediaListeners = new Map();
1304
+ textOperators = ['contains', 'doesNotContain', 'startsWith', 'endsWith', 'eq', 'neq', 'isEmpty', 'isNotEmpty'];
1305
+ numericOperators = ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'between', 'isNull', 'isNotNull'];
1306
+ booleanOperators = ['eq', 'neq'];
1307
+ constructor(cdr, stateStore, uiConfig, messages) {
1308
+ this.cdr = cdr;
1309
+ this.stateStore = stateStore;
1310
+ this.uiConfig = uiConfig;
1311
+ this.messages = messages;
1312
+ }
1313
+ get effectiveLocale() {
1314
+ return this.locale ?? this.uiConfig.locale;
1315
+ }
1316
+ get effectiveDir() {
1317
+ return this.dir ?? this.uiConfig.direction;
1318
+ }
1319
+ get effectiveCommandColumnTitle() {
1320
+ return this.commandColumnTitle ?? this.messages.commandColumn;
1321
+ }
1322
+ get formatOptions() {
1323
+ return { locale: this.effectiveLocale, yes: this.messages.yes, no: this.messages.no };
1324
+ }
1325
+ ngOnInit() {
1326
+ this.syncMediaQueries();
1327
+ }
1328
+ ngOnDestroy() {
1329
+ for (const [query, listener] of this.mediaListeners) {
1330
+ this.mediaQueryLists.get(query)?.removeEventListener('change', listener);
1331
+ }
1332
+ this.mediaListeners.clear();
1333
+ this.mediaQueryLists.clear();
1334
+ }
1335
+ ngOnChanges(changes) {
1336
+ if (changes['columns']) {
1337
+ this.internalColumns = (this.columns ?? []).map((column) => ({
1338
+ sortable: true,
1339
+ filterable: true,
1340
+ groupable: true,
1341
+ resizable: true,
1342
+ reorderable: true,
1343
+ menu: true,
1344
+ exportable: true,
1345
+ ...column,
1346
+ }));
1347
+ this.applyProjectedTemplates();
1348
+ this.ensureFilterDefaults();
1349
+ this.restorePersistedState();
1350
+ this.syncMediaQueries();
1351
+ }
1352
+ if (changes['state'] && !this.state) {
1353
+ this.state = mbGridDefaultState();
1354
+ }
1355
+ this.reprocess();
1356
+ }
1357
+ ngAfterContentInit() {
1358
+ this.applyProjectedTemplates();
1359
+ this.cellTemplates?.changes.subscribe(() => this.applyProjectedTemplates());
1360
+ this.headerTemplates?.changes.subscribe(() => this.applyProjectedTemplates());
1361
+ this.editTemplates?.changes.subscribe(() => this.applyProjectedTemplates());
1362
+ this.footerTemplates?.changes.subscribe(() => this.applyProjectedTemplates());
1363
+ this.reprocess();
1364
+ this.cdr.markForCheck();
1365
+ }
1366
+ get visibleColumns() {
1367
+ return this.internalColumns.filter((column) => !column.hidden && this.matchesMedia(column));
1368
+ }
1369
+ get lockedStartColumns() {
1370
+ return this.visibleColumns.filter((column) => this.isLockedStart(column));
1371
+ }
1372
+ get lockedEndColumns() {
1373
+ return this.visibleColumns.filter((column) => this.isLockedEnd(column));
1374
+ }
1375
+ get hasFilterRow() {
1376
+ return this.filterable && (this.filterMode === 'row' || this.filterMode === 'both');
1377
+ }
1378
+ get hasColumnMenu() {
1379
+ return this.columnMenu && (this.filterMode === 'menu' || this.filterMode === 'both' || this.showColumnChooser || this.groupable);
1380
+ }
1381
+ get pageIndex() {
1382
+ return Math.floor((this.state?.skip ?? 0) / (this.state?.take || 1));
1383
+ }
1384
+ get pageSize() {
1385
+ return this.state?.take ?? 10;
1386
+ }
1387
+ get effectiveTotal() {
1388
+ return this.dataMode === 'server' ? this.total : this.processed.total;
1389
+ }
1390
+ get pageCount() {
1391
+ return Math.max(1, Math.ceil(this.effectiveTotal / this.pageSize));
1392
+ }
1393
+ get from() {
1394
+ if (!this.effectiveTotal)
1395
+ return 0;
1396
+ return this.pageIndex * this.pageSize + 1;
1397
+ }
1398
+ get to() {
1399
+ return Math.min((this.pageIndex + 1) * this.pageSize, this.effectiveTotal);
1400
+ }
1401
+ get gridStyle() {
1402
+ return { height: typeof this.height === 'number' ? `${this.height}px` : this.height };
1403
+ }
1404
+ get allVisibleRowsSelected() {
1405
+ const dataRows = this.dataRowsFromRendered();
1406
+ return dataRows.length > 0 && dataRows.every((row) => this.selectedKeys.includes(this.getRowKey(row)));
1407
+ }
1408
+ get someVisibleRowsSelected() {
1409
+ const dataRows = this.dataRowsFromRendered();
1410
+ return dataRows.some((row) => this.selectedKeys.includes(this.getRowKey(row))) && !this.allVisibleRowsSelected;
1411
+ }
1412
+ get toolbarContext() {
1413
+ return {
1414
+ state: this.state,
1415
+ total: this.effectiveTotal,
1416
+ selectedKeys: this.selectedKeys,
1417
+ selectedRows: this.selectedRows,
1418
+ add: () => this.createRow(),
1419
+ exportCsv: () => this.exportCsv(),
1420
+ exportExcel: () => this.exportExcel(),
1421
+ reset: () => this.resetState(),
1422
+ };
1423
+ }
1424
+ get selectedRows() {
1425
+ const allRows = this.data ?? [];
1426
+ return allRows.filter((row) => this.selectedKeys.includes(this.getRowKey(row)));
1427
+ }
1428
+ get columnSpan() {
1429
+ return this.visibleColumns.length + (this.selectable ? 1 : 0) + (this.expandable ? 1 : 0) + (this.showCommandColumn ? 1 : 0);
1430
+ }
1431
+ get showCommandColumn() {
1432
+ return this.editable || this.allowDelete;
1433
+ }
1434
+ getOperatorLabel(operator) {
1435
+ return this.messages.operatorLabels[operator] ?? operator;
1436
+ }
1437
+ getOperators(column) {
1438
+ if (column.type === 'number' || column.type === 'date')
1439
+ return this.numericOperators;
1440
+ if (column.type === 'boolean')
1441
+ return this.booleanOperators;
1442
+ return this.textOperators;
1443
+ }
1444
+ getColumnStyle(column) {
1445
+ const lockedStart = this.isLockedStart(column);
1446
+ const lockedEnd = this.isLockedEnd(column);
1447
+ return {
1448
+ width: this.toCssSize(column.width),
1449
+ minWidth: this.toCssSize(column.minWidth),
1450
+ maxWidth: this.toCssSize(column.maxWidth),
1451
+ position: lockedStart || lockedEnd ? 'sticky' : null,
1452
+ insetInlineStart: lockedStart ? `${this.getLockedOffset(column, 'start')}px` : null,
1453
+ insetInlineEnd: lockedEnd ? `${this.getLockedOffset(column, 'end')}px` : null,
1454
+ zIndex: lockedStart || lockedEnd ? '6' : null,
1455
+ };
1456
+ }
1457
+ getColumnClass(column) {
1458
+ return {
1459
+ 'mb-grid__cell--locked-start': this.isLockedStart(column),
1460
+ 'mb-grid__cell--locked-end': this.isLockedEnd(column),
1461
+ };
1462
+ }
1463
+ isLockedStart(column) {
1464
+ return !!column.locked || column.sticky === 'start';
1465
+ }
1466
+ isLockedEnd(column) {
1467
+ return column.sticky === 'end';
1468
+ }
1469
+ matchesMedia(column) {
1470
+ if (!this.responsiveColumns || !column.media || typeof window === 'undefined') {
1471
+ return true;
1472
+ }
1473
+ return this.mediaQueryLists.get(column.media)?.matches ?? true;
1474
+ }
1475
+ pagerInfoText() {
1476
+ return this.messages.pagerInfo(this.from, this.to, this.effectiveTotal);
1477
+ }
1478
+ getSortIndex(column) {
1479
+ return (this.state.sort ?? []).findIndex((item) => String(item.field) === String(column.field));
1480
+ }
1481
+ getSortDirection(column) {
1482
+ return this.state.sort?.find((item) => String(item.field) === String(column.field))?.dir ?? '';
1483
+ }
1484
+ getValue(row, column, rowIndex = -1) {
1485
+ return column.valueGetter ? column.valueGetter(row, rowIndex) : mbGridGetValue(row, column.field);
1486
+ }
1487
+ formatCell(row, column, rowIndex) {
1488
+ return mbGridFormatValue(row, column, rowIndex, this.formatOptions);
1489
+ }
1490
+ getCellClass(row, column, rowIndex) {
1491
+ if (!column.className)
1492
+ return null;
1493
+ if (typeof column.className === 'function') {
1494
+ return column.className(row, this.getValue(row, column, rowIndex), rowIndex);
1495
+ }
1496
+ return column.className;
1497
+ }
1498
+ getCellTemplate(column) {
1499
+ return column.cellTemplate ?? null;
1500
+ }
1501
+ getHeaderTemplate(column) {
1502
+ return column.headerTemplate ?? null;
1503
+ }
1504
+ getFooterTemplate(column) {
1505
+ return column.footerTemplate ?? null;
1506
+ }
1507
+ getRowKey(row) {
1508
+ if (typeof this.rowKey === 'function')
1509
+ return this.rowKey(row);
1510
+ return mbGridGetValue(row, this.rowKey);
1511
+ }
1512
+ toggleSort(column, event) {
1513
+ if (!this.sortable || column.sortable === false)
1514
+ return;
1515
+ const field = String(column.field);
1516
+ const current = [...(this.state.sort ?? [])];
1517
+ const index = current.findIndex((item) => String(item.field) === field);
1518
+ const isMulti = this.multiSort && !!event?.shiftKey;
1519
+ const base = isMulti ? current : index >= 0 ? [current[index]] : [];
1520
+ const targetIndex = base.findIndex((item) => String(item.field) === field);
1521
+ if (targetIndex < 0) {
1522
+ base.push({ field: column.field, dir: 'asc' });
1523
+ }
1524
+ else if (base[targetIndex].dir === 'asc') {
1525
+ base[targetIndex] = { ...base[targetIndex], dir: 'desc' };
1526
+ }
1527
+ else {
1528
+ base.splice(targetIndex, 1);
1529
+ }
1530
+ this.setState({ ...this.state, sort: base, skip: 0 });
1531
+ this.sortChange.emit({ sort: base });
1532
+ }
1533
+ setFilterValue(column, value) {
1534
+ const field = String(column.field);
1535
+ const current = this.filtersByField[field] ?? { operator: this.defaultOperator(column), value: '' };
1536
+ this.filtersByField = { ...this.filtersByField, [field]: { ...current, value } };
1537
+ this.applyFilters();
1538
+ }
1539
+ setFilterOperator(column, operator) {
1540
+ const field = String(column.field);
1541
+ const current = this.filtersByField[field] ?? { operator, value: '' };
1542
+ this.filtersByField = { ...this.filtersByField, [field]: { ...current, operator } };
1543
+ this.applyFilters();
1544
+ }
1545
+ clearFilter(column) {
1546
+ if (!column) {
1547
+ this.filtersByField = {};
1548
+ }
1549
+ else {
1550
+ const clone = { ...this.filtersByField };
1551
+ delete clone[String(column.field)];
1552
+ this.filtersByField = clone;
1553
+ }
1554
+ this.applyFilters();
1555
+ }
1556
+ applyGlobalSearch(value) {
1557
+ this.globalSearch = value;
1558
+ const filters = this.visibleColumns
1559
+ .filter((column) => column.filterable !== false && column.type !== 'custom')
1560
+ .map((column) => ({ field: column.field, operator: 'contains', value, ignoreCase: true }));
1561
+ const columnFilter = mbGridBuildFilter(this.filtersByField);
1562
+ const globalFilter = value?.trim()
1563
+ ? { logic: 'or', filters }
1564
+ : undefined;
1565
+ const filter = columnFilter && globalFilter
1566
+ ? { logic: 'and', filters: [columnFilter, globalFilter] }
1567
+ : columnFilter ?? globalFilter;
1568
+ this.setState({ ...this.state, filter, skip: 0 });
1569
+ this.filterChange.emit({ filter });
1570
+ }
1571
+ changePage(pageIndex) {
1572
+ const nextPage = Math.min(Math.max(pageIndex, 0), this.pageCount - 1);
1573
+ const state = { ...this.state, skip: nextPage * this.pageSize };
1574
+ this.setState(state);
1575
+ this.pageChange.emit({ pageIndex: nextPage, pageSize: this.pageSize, skip: state.skip, take: state.take });
1576
+ }
1577
+ changePageSize(value) {
1578
+ const take = Number(value);
1579
+ const state = { ...this.state, skip: 0, take };
1580
+ this.setState(state);
1581
+ this.pageChange.emit({ pageIndex: 0, pageSize: take, skip: 0, take });
1582
+ }
1583
+ toggleRow(row, event) {
1584
+ event?.stopPropagation();
1585
+ const key = this.getRowKey(row);
1586
+ const exists = this.selectedKeys.includes(key);
1587
+ const next = this.selectionMode === 'single'
1588
+ ? exists ? [] : [key]
1589
+ : exists ? this.selectedKeys.filter((item) => item !== key) : [...this.selectedKeys, key];
1590
+ this.selectedKeys = next;
1591
+ this.selectedKeysChange.emit(next);
1592
+ this.selectionChange.emit({ selectedKeys: next, selectedRows: this.selectedRows });
1593
+ }
1594
+ toggleAllVisible(event) {
1595
+ event?.stopPropagation();
1596
+ const visible = this.dataRowsFromRendered().map((row) => this.getRowKey(row));
1597
+ const allSelected = visible.every((key) => this.selectedKeys.includes(key));
1598
+ this.selectedKeys = allSelected
1599
+ ? this.selectedKeys.filter((key) => !visible.includes(key))
1600
+ : Array.from(new Set([...this.selectedKeys, ...visible]));
1601
+ this.selectedKeysChange.emit(this.selectedKeys);
1602
+ this.selectionChange.emit({ selectedKeys: this.selectedKeys, selectedRows: this.selectedRows });
1603
+ }
1604
+ isSelected(row) {
1605
+ return this.selectedKeys.includes(this.getRowKey(row));
1606
+ }
1607
+ onRowClick(row, rowIndex) {
1608
+ this.rowClick.emit({ row, rowIndex });
1609
+ }
1610
+ onCellClick(row, column, rowIndex) {
1611
+ this.cellClick.emit({ row, column, value: this.getValue(row, column, rowIndex), rowIndex });
1612
+ }
1613
+ toggleColumnMenu(column, event) {
1614
+ event.stopPropagation();
1615
+ const field = String(column.field);
1616
+ this.openColumnMenuField = this.openColumnMenuField === field ? '' : field;
1617
+ }
1618
+ hideColumn(column) {
1619
+ column.hidden = true;
1620
+ this.openColumnMenuField = '';
1621
+ this.columnVisibilityChange.emit({ column, field: column.field, visible: false, columns: this.internalColumns });
1622
+ this.persistState();
1623
+ this.cdr.markForCheck();
1624
+ }
1625
+ showColumn(column, visible) {
1626
+ column.hidden = !visible;
1627
+ this.columnVisibilityChange.emit({ column, field: column.field, visible, columns: this.internalColumns });
1628
+ this.persistState();
1629
+ this.cdr.markForCheck();
1630
+ }
1631
+ toggleLock(column) {
1632
+ column.locked = !column.locked;
1633
+ column.sticky = column.locked ? 'start' : false;
1634
+ this.columnLockChange.emit({ column, field: column.field, locked: !!column.locked, columns: this.internalColumns });
1635
+ this.persistState();
1636
+ this.cdr.markForCheck();
1637
+ }
1638
+ resetColumns() {
1639
+ this.internalColumns = (this.columns ?? []).map((column) => ({ ...column }));
1640
+ this.applyProjectedTemplates();
1641
+ this.cdr.markForCheck();
1642
+ }
1643
+ toggleGroup(column) {
1644
+ if (!this.groupable || column.groupable === false)
1645
+ return;
1646
+ const field = String(column.field);
1647
+ const exists = this.state.group?.some((group) => String(group.field) === field);
1648
+ const group = exists
1649
+ ? this.state.group.filter((item) => String(item.field) !== field)
1650
+ : [...(this.state.group ?? []), { field: column.field, dir: 'asc' }];
1651
+ this.setState({ ...this.state, group, skip: 0 });
1652
+ this.groupChange.emit({ group });
1653
+ this.openColumnMenuField = '';
1654
+ }
1655
+ removeGroup(group) {
1656
+ const next = this.state.group.filter((item) => String(item.field) !== String(group.field));
1657
+ this.setState({ ...this.state, group: next, skip: 0 });
1658
+ this.groupChange.emit({ group: next });
1659
+ }
1660
+ toggleGroupRow(row) {
1661
+ if (row.kind !== 'group')
1662
+ return;
1663
+ if (this.expandedGroupKeys.has(row.key))
1664
+ this.expandedGroupKeys.delete(row.key);
1665
+ else
1666
+ this.expandedGroupKeys.add(row.key);
1667
+ this.reprocess();
1668
+ }
1669
+ startResize(column, event) {
1670
+ if (!this.resizable || column.resizable === false)
1671
+ return;
1672
+ event.preventDefault();
1673
+ event.stopPropagation();
1674
+ const header = event.target.closest('th');
1675
+ this.resizing = { field: String(column.field), startX: event.clientX, startWidth: header?.offsetWidth ?? 120 };
1676
+ const move = (moveEvent) => this.resizeColumn(moveEvent);
1677
+ const up = () => {
1678
+ document.removeEventListener('mousemove', move);
1679
+ document.removeEventListener('mouseup', up);
1680
+ this.resizing = undefined;
1681
+ };
1682
+ document.addEventListener('mousemove', move);
1683
+ document.addEventListener('mouseup', up);
1684
+ }
1685
+ dropColumn(event) {
1686
+ if (!this.reorderable || event.previousIndex === event.currentIndex)
1687
+ return;
1688
+ moveItemInArray(this.internalColumns, event.previousIndex, event.currentIndex);
1689
+ this.columnReorder.emit({ previousIndex: event.previousIndex, currentIndex: event.currentIndex, columns: this.internalColumns });
1690
+ this.persistState();
1691
+ this.cdr.markForCheck();
1692
+ }
1693
+ exportCsv() {
1694
+ const rows = this.exportRows();
1695
+ mbGridExportCsv(rows, this.visibleColumns, this.exportFileName, this.formatOptions);
1696
+ }
1697
+ exportExcel() {
1698
+ mbGridExportExcelHtml(this.exportRows(), this.visibleColumns, this.excelFileName, this.formatOptions, this.effectiveDir);
1699
+ }
1700
+ exportJson() {
1701
+ mbGridExportJson(this.exportRows(), this.visibleColumns, this.jsonFileName);
1702
+ }
1703
+ resetState() {
1704
+ this.globalSearch = '';
1705
+ this.filtersByField = {};
1706
+ this.expandedGroupKeys.clear();
1707
+ this.expandedDetailKeys.clear();
1708
+ this.cancelEditInternal(false);
1709
+ this.setState(mbGridDefaultState(this.pageSize));
1710
+ }
1711
+ createRow() {
1712
+ if (!this.editable || !this.allowAdd)
1713
+ return;
1714
+ const row = this.newItemFactory ? this.newItemFactory() : {};
1715
+ const key = `__new_${Date.now()}`;
1716
+ this.editingKey = key;
1717
+ this.editingModel = this.cloneRow(row);
1718
+ this.originalModel = null;
1719
+ this.editingRowIndex = 0;
1720
+ this.editingIsNew = true;
1721
+ this.validationErrors = {};
1722
+ this.cdr.markForCheck();
1723
+ }
1724
+ startEdit(row, rowIndex, event) {
1725
+ event?.stopPropagation();
1726
+ if (!this.editable || this.editMode === 'none')
1727
+ return;
1728
+ const key = this.getRowKey(row);
1729
+ this.editingKey = key;
1730
+ this.editingModel = this.cloneRow(row);
1731
+ this.originalModel = this.cloneRow(row);
1732
+ this.editingRowIndex = rowIndex;
1733
+ this.editingIsNew = false;
1734
+ this.validationErrors = {};
1735
+ this.edit.emit({ row, rowIndex, key });
1736
+ this.cdr.markForCheck();
1737
+ }
1738
+ saveEdit(event) {
1739
+ event?.stopPropagation();
1740
+ if (!this.editingModel || this.editingKey === null)
1741
+ return;
1742
+ if (!this.validateEditingModel()) {
1743
+ this.cdr.markForCheck();
1744
+ return;
1745
+ }
1746
+ const row = this.cloneRow(this.editingModel);
1747
+ const key = this.editingKey;
1748
+ this.save.emit({ row, originalRow: this.originalModel, rowIndex: this.editingRowIndex, key, isNew: this.editingIsNew });
1749
+ this.cancelEditInternal(false);
1750
+ this.cdr.markForCheck();
1751
+ }
1752
+ cancelEditRow(event) {
1753
+ event?.stopPropagation();
1754
+ if (!this.editingModel || this.editingKey === null)
1755
+ return;
1756
+ this.cancel.emit({ row: this.editingModel, originalRow: this.originalModel, rowIndex: this.editingRowIndex, key: this.editingKey, isNew: this.editingIsNew });
1757
+ this.cancelEditInternal(true);
1758
+ }
1759
+ removeRow(row, rowIndex, event) {
1760
+ event?.stopPropagation();
1761
+ if (this.confirmDelete && !confirm(this.messages.deleteConfirm))
1762
+ return;
1763
+ this.remove.emit({ row, rowIndex, key: this.getRowKey(row) });
1764
+ }
1765
+ isEditing(row) {
1766
+ return this.editingKey !== null && this.getRowKey(row) === this.editingKey;
1767
+ }
1768
+ isNewRowEditing() {
1769
+ return this.editingIsNew && !!this.editingModel;
1770
+ }
1771
+ setEditValue(column, value) {
1772
+ if (!this.editingModel)
1773
+ return;
1774
+ if (column.valueSetter) {
1775
+ this.editingModel = column.valueSetter(this.editingModel, value, this.editingRowIndex);
1776
+ }
1777
+ else {
1778
+ this.setNestedValue(this.editingModel, String(column.field), value);
1779
+ }
1780
+ delete this.validationErrors[String(column.field)];
1781
+ this.cdr.markForCheck();
1782
+ }
1783
+ getEditValue(column) {
1784
+ return this.editingModel ? this.getValue(this.editingModel, column, this.editingRowIndex) : undefined;
1785
+ }
1786
+ canEditColumn(column) {
1787
+ return this.editable && column.editable !== false && column.type !== 'custom';
1788
+ }
1789
+ getEditorType(column) {
1790
+ if (column.editor)
1791
+ return column.editor;
1792
+ if (column.type === 'number')
1793
+ return 'number';
1794
+ if (column.type === 'date')
1795
+ return 'date';
1796
+ if (column.type === 'boolean')
1797
+ return 'checkbox';
1798
+ if (column.type === 'enum')
1799
+ return 'select';
1800
+ return 'text';
1801
+ }
1802
+ toggleDetail(row, rowIndex, event) {
1803
+ event?.stopPropagation();
1804
+ const key = this.getRowKey(row);
1805
+ const expanded = !this.expandedDetailKeys.has(key);
1806
+ if (expanded)
1807
+ this.expandedDetailKeys.add(key);
1808
+ else
1809
+ this.expandedDetailKeys.delete(key);
1810
+ this.detailToggle.emit({ row, rowIndex, key, expanded });
1811
+ this.cdr.markForCheck();
1812
+ }
1813
+ isDetailExpanded(row) {
1814
+ const key = this.getRowKey(row);
1815
+ return this.detailInitiallyExpanded || this.expandedDetailKeys.has(key);
1816
+ }
1817
+ getRowClass(row, rowIndex) {
1818
+ if (!this.rowClassName)
1819
+ return null;
1820
+ return typeof this.rowClassName === 'function' ? this.rowClassName(row, rowIndex) : this.rowClassName;
1821
+ }
1822
+ trackColumn(_, column) {
1823
+ return String(column.field);
1824
+ }
1825
+ trackRow(_, row) {
1826
+ return row.kind === 'group' ? row.key : this.getRowKey(row.dataItem);
1827
+ }
1828
+ toCssSize(value) {
1829
+ if (value === undefined || value === null || value === '')
1830
+ return null;
1831
+ return typeof value === 'number' ? `${value}px` : value;
1832
+ }
1833
+ getLockedOffset(column, side) {
1834
+ const columns = side === 'start' ? this.lockedStartColumns : this.lockedEndColumns;
1835
+ let offset = 0;
1836
+ for (const item of columns) {
1837
+ if (String(item.field) === String(column.field))
1838
+ break;
1839
+ offset += this.resolveColumnWidth(item);
1840
+ }
1841
+ return offset;
1842
+ }
1843
+ resolveColumnWidth(column) {
1844
+ if (typeof column.width === 'number')
1845
+ return column.width;
1846
+ if (typeof column.width === 'string' && column.width.endsWith('px')) {
1847
+ return Number.parseFloat(column.width);
1848
+ }
1849
+ if (typeof column.minWidth === 'number')
1850
+ return column.minWidth;
1851
+ return 120;
1852
+ }
1853
+ syncMediaQueries() {
1854
+ if (!this.responsiveColumns || typeof window === 'undefined')
1855
+ return;
1856
+ const queries = new Set(this.internalColumns
1857
+ .map((column) => column.media)
1858
+ .filter((media) => !!media));
1859
+ for (const query of queries) {
1860
+ if (this.mediaQueryLists.has(query))
1861
+ continue;
1862
+ const mediaQueryList = window.matchMedia(query);
1863
+ const listener = () => {
1864
+ this.cdr.markForCheck();
1865
+ };
1866
+ mediaQueryList.addEventListener('change', listener);
1867
+ this.mediaQueryLists.set(query, mediaQueryList);
1868
+ this.mediaListeners.set(query, listener);
1869
+ }
1870
+ }
1871
+ setState(state) {
1872
+ this.state = { ...state, sort: state.sort ?? [], group: state.group ?? [] };
1873
+ this.stateChange.emit(this.state);
1874
+ this.persistState();
1875
+ this.reprocess();
1876
+ }
1877
+ reprocess() {
1878
+ const safeState = this.state ?? mbGridDefaultState();
1879
+ if (this.dataMode === 'server') {
1880
+ this.processed = {
1881
+ data: (this.data ?? []).map((dataItem, rowIndex) => ({ kind: 'data', dataItem, rowIndex: safeState.skip + rowIndex })),
1882
+ total: this.total,
1883
+ aggregates: {},
1884
+ };
1885
+ }
1886
+ else {
1887
+ this.processed = mbGridProcess(this.data ?? [], safeState, this.internalColumns, this.expandedGroupKeys);
1888
+ }
1889
+ this.cdr.markForCheck();
1890
+ }
1891
+ applyFilters() {
1892
+ const columnFilter = mbGridBuildFilter(this.filtersByField);
1893
+ if (this.globalSearch) {
1894
+ this.applyGlobalSearch(this.globalSearch);
1895
+ return;
1896
+ }
1897
+ this.setState({ ...this.state, filter: columnFilter, skip: 0 });
1898
+ this.filterChange.emit({ filter: columnFilter });
1899
+ }
1900
+ defaultOperator(column) {
1901
+ if (column.type === 'number' || column.type === 'date' || column.type === 'boolean')
1902
+ return 'eq';
1903
+ return 'contains';
1904
+ }
1905
+ ensureFilterDefaults() {
1906
+ const next = { ...this.filtersByField };
1907
+ for (const column of this.internalColumns) {
1908
+ const field = String(column.field);
1909
+ next[field] ??= { operator: this.defaultOperator(column), value: '' };
1910
+ }
1911
+ this.filtersByField = next;
1912
+ }
1913
+ applyProjectedTemplates() {
1914
+ for (const column of this.internalColumns) {
1915
+ const field = String(column.field);
1916
+ const cell = this.cellTemplates?.find((item) => item.field === field);
1917
+ const header = this.headerTemplates?.find((item) => item.field === field);
1918
+ const footer = this.footerTemplates?.find((item) => item.field === field);
1919
+ const edit = this.editTemplates?.find((item) => item.field === field);
1920
+ if (cell)
1921
+ column.cellTemplate = cell.templateRef;
1922
+ if (header)
1923
+ column.headerTemplate = header.templateRef;
1924
+ if (footer)
1925
+ column.footerTemplate = footer.templateRef;
1926
+ if (edit)
1927
+ column.editTemplate = edit.templateRef;
1928
+ }
1929
+ }
1930
+ resizeColumn(event) {
1931
+ if (!this.resizing)
1932
+ return;
1933
+ const delta = this.effectiveDir === 'rtl' ? this.resizing.startX - event.clientX : event.clientX - this.resizing.startX;
1934
+ const width = Math.max(56, this.resizing.startWidth + delta);
1935
+ const column = this.internalColumns.find((item) => String(item.field) === this.resizing?.field);
1936
+ if (!column)
1937
+ return;
1938
+ column.width = width;
1939
+ this.columnResize.emit({ column, field: column.field, width });
1940
+ this.cdr.markForCheck();
1941
+ }
1942
+ exportRows() {
1943
+ if (this.dataMode === 'server')
1944
+ return this.data ?? [];
1945
+ const safeState = { ...this.state, skip: 0, take: Number.MAX_SAFE_INTEGER };
1946
+ return mbGridProcess(this.data ?? [], safeState, this.internalColumns, this.expandedGroupKeys)
1947
+ .data
1948
+ .filter((row) => row.kind === 'data')
1949
+ .map((row) => row.dataItem);
1950
+ }
1951
+ validateEditingModel() {
1952
+ if (!this.editingModel)
1953
+ return true;
1954
+ const errors = {};
1955
+ for (const column of this.visibleColumns) {
1956
+ if (!this.canEditColumn(column))
1957
+ continue;
1958
+ const value = this.getValue(this.editingModel, column, this.editingRowIndex);
1959
+ const field = String(column.field);
1960
+ if (column.required && (value === null || value === undefined || String(value).trim() === '')) {
1961
+ errors[field] = this.messages.fieldRequired(column.title);
1962
+ continue;
1963
+ }
1964
+ const custom = column.validator?.(value, this.editingModel, column);
1965
+ if (custom)
1966
+ errors[field] = custom;
1967
+ }
1968
+ this.validationErrors = errors;
1969
+ return Object.keys(errors).length === 0;
1970
+ }
1971
+ cloneRow(row) {
1972
+ if (typeof structuredClone === 'function')
1973
+ return structuredClone(row);
1974
+ return JSON.parse(JSON.stringify(row));
1975
+ }
1976
+ setNestedValue(row, path, value) {
1977
+ const parts = path.split('.');
1978
+ let cursor = row;
1979
+ for (let index = 0; index < parts.length - 1; index++) {
1980
+ const part = parts[index];
1981
+ cursor[part] ??= {};
1982
+ cursor = cursor[part];
1983
+ }
1984
+ cursor[parts[parts.length - 1]] = value;
1985
+ }
1986
+ cancelEditInternal(mark = true) {
1987
+ this.editingKey = null;
1988
+ this.editingModel = null;
1989
+ this.originalModel = null;
1990
+ this.editingRowIndex = -1;
1991
+ this.editingIsNew = false;
1992
+ this.validationErrors = {};
1993
+ if (mark)
1994
+ this.cdr.markForCheck();
1995
+ }
1996
+ restorePersistedState() {
1997
+ if (!this.persistKey)
1998
+ return;
1999
+ const saved = this.stateStore.load(this.persistKey);
2000
+ if (!saved)
2001
+ return;
2002
+ this.state = saved.state ?? this.state;
2003
+ this.selectedKeys = saved.selectedKeys ?? this.selectedKeys;
2004
+ for (const savedColumn of saved.columns ?? []) {
2005
+ const target = this.internalColumns.find((column) => String(column.field) === String(savedColumn.field));
2006
+ if (!target)
2007
+ continue;
2008
+ target.width = savedColumn.width ?? target.width;
2009
+ target.hidden = savedColumn.hidden ?? target.hidden;
2010
+ target.locked = savedColumn.locked ?? target.locked;
2011
+ target.sticky = savedColumn.sticky ?? target.sticky;
2012
+ }
2013
+ }
2014
+ persistState() {
2015
+ if (!this.autoPersist || !this.persistKey)
2016
+ return;
2017
+ this.stateStore.save(this.persistKey, this.state, this.internalColumns, this.selectedKeys);
2018
+ }
2019
+ dataRowsFromRendered() {
2020
+ return this.processed.data.filter((row) => row.kind === 'data').map((row) => row.dataItem);
2021
+ }
2022
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbGridComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: MbGridStateService }, { token: MB_UI_CONFIG }, { token: MB_GRID_MESSAGES }], target: i0.ɵɵFactoryTarget.Component });
2023
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbGridComponent, isStandalone: true, selector: "mb-grid", inputs: { data: "data", columns: "columns", loading: "loading", dataMode: "dataMode", state: "state", total: "total", height: "height", rowHeight: "rowHeight", density: "density", locale: "locale", dir: "dir", noRecordsText: "noRecordsText", loadingText: "loadingText", pageable: "pageable", pageSizeOptions: "pageSizeOptions", sortable: "sortable", multiSort: "multiSort", filterable: "filterable", filterMode: "filterMode", groupable: "groupable", selectable: "selectable", selectionMode: "selectionMode", rowKey: "rowKey", selectedKeys: "selectedKeys", resizable: "resizable", reorderable: "reorderable", columnMenu: "columnMenu", showToolbar: "showToolbar", showSearch: "showSearch", showColumnChooser: "showColumnChooser", showExport: "showExport", showFooter: "showFooter", virtualScroll: "virtualScroll", exportFileName: "exportFileName", excelFileName: "excelFileName", jsonFileName: "jsonFileName", showExcelExport: "showExcelExport", showJsonExport: "showJsonExport", persistKey: "persistKey", autoPersist: "autoPersist", editable: "editable", editMode: "editMode", allowAdd: "allowAdd", allowDelete: "allowDelete", confirmDelete: "confirmDelete", commandColumnTitle: "commandColumnTitle", newItemFactory: "newItemFactory", expandable: "expandable", detailInitiallyExpanded: "detailInitiallyExpanded", rowClassName: "rowClassName", responsiveColumns: "responsiveColumns" }, outputs: { stateChange: "stateChange", pageChange: "pageChange", sortChange: "sortChange", filterChange: "filterChange", groupChange: "groupChange", selectedKeysChange: "selectedKeysChange", columnResize: "columnResize", columnReorder: "columnReorder", columnVisibilityChange: "columnVisibilityChange", columnLockChange: "columnLockChange", selectionChange: "selectionChange", edit: "edit", save: "save", cancel: "cancel", remove: "remove", detailToggle: "detailToggle", rowClick: "rowClick", cellClick: "cellClick" }, queries: [{ propertyName: "toolbarTemplate", first: true, predicate: MbGridToolbarTemplateDirective, descendants: true }, { propertyName: "detailTemplate", first: true, predicate: MbGridDetailTemplateDirective, descendants: true }, { propertyName: "cellTemplates", predicate: MbGridCellTemplateDirective }, { propertyName: "headerTemplates", predicate: MbGridHeaderTemplateDirective }, { propertyName: "editTemplates", predicate: MbGridEditTemplateDirective }, { propertyName: "footerTemplates", predicate: MbGridFooterTemplateDirective }], usesOnChanges: true, ngImport: i0, template: "<div class=\"mb-grid\" [attr.dir]=\"effectiveDir\" [ngClass]=\"['mb-grid--' + density, loading ? 'mb-grid--loading' : '']\" [ngStyle]=\"gridStyle\">\n <div *ngIf=\"showToolbar\" class=\"mb-grid__toolbar\">\n <ng-container *ngIf=\"toolbarTemplate?.templateRef as toolbarTpl; else defaultToolbar\">\n <ng-container [ngTemplateOutlet]=\"toolbarTpl\" [ngTemplateOutletContext]=\"toolbarContext\"></ng-container>\n </ng-container>\n\n <ng-template #defaultToolbar>\n <div class=\"mb-grid__toolbar-start\">\n <div *ngIf=\"groupable\" class=\"mb-grid__group-panel\">\n <span class=\"mb-grid__group-label\">{{ messages.groupLabel }}</span>\n <button\n *ngFor=\"let group of state.group\"\n type=\"button\"\n class=\"mb-grid__group-chip\"\n (click)=\"removeGroup(group)\">\n {{ group.field }} \u00D7\n </button>\n <span *ngIf=\"!state.group.length\" class=\"mb-grid__group-hint\">{{ messages.groupHint }}</span>\n </div>\n </div>\n\n <div class=\"mb-grid__toolbar-end\">\n <button *ngIf=\"editable && allowAdd\" type=\"button\" class=\"mb-grid__tool-button mb-grid__tool-button--primary\" (click)=\"createRow()\">{{ messages.addRow }}</button>\n <input\n *ngIf=\"showSearch\"\n class=\"mb-grid__search\"\n [ngModel]=\"globalSearch\"\n (ngModelChange)=\"applyGlobalSearch($event)\"\n [placeholder]=\"messages.searchPlaceholder\" />\n\n <div *ngIf=\"showColumnChooser\" class=\"mb-grid__chooser\">\n <button type=\"button\" class=\"mb-grid__tool-button\" (click)=\"openColumnMenuField = openColumnMenuField === '__chooser' ? '' : '__chooser'\">\n {{ messages.columns }}\n </button>\n <div *ngIf=\"openColumnMenuField === '__chooser'\" class=\"mb-grid__chooser-menu\" (click)=\"$event.stopPropagation()\">\n <label *ngFor=\"let column of internalColumns\" class=\"mb-grid__chooser-option\">\n <input type=\"checkbox\" [checked]=\"!column.hidden\" (change)=\"showColumn(column, $any($event.target).checked)\" />\n <span>{{ column.title }}</span>\n </label>\n <button type=\"button\" class=\"mb-grid__menu-action\" (click)=\"resetColumns()\">{{ messages.resetColumns }}</button>\n </div>\n </div>\n\n <button *ngIf=\"showExport\" type=\"button\" class=\"mb-grid__tool-button\" (click)=\"exportCsv()\">{{ messages.exportCsv }}</button>\n <button *ngIf=\"showExport && showExcelExport\" type=\"button\" class=\"mb-grid__tool-button\" (click)=\"exportExcel()\">{{ messages.exportExcel }}</button>\n <button *ngIf=\"showExport && showJsonExport\" type=\"button\" class=\"mb-grid__tool-button\" (click)=\"exportJson()\">{{ messages.exportJson }}</button>\n <button type=\"button\" class=\"mb-grid__tool-button\" (click)=\"resetState()\">{{ messages.reset }}</button>\n </div>\n </ng-template>\n </div>\n\n <div *ngIf=\"loading\" class=\"mb-grid__loading\" aria-live=\"polite\">\n <span class=\"mb-grid__spinner\"></span>\n <span>{{ loadingText || messages.loading }}</span>\n </div>\n\n <div class=\"mb-grid__scroll\">\n <table class=\"mb-grid__table\">\n <thead>\n <tr\n class=\"mb-grid__header-row\"\n cdkDropList\n cdkDropListOrientation=\"horizontal\"\n [cdkDropListDisabled]=\"!reorderable\"\n [cdkDropListData]=\"visibleColumns\"\n (cdkDropListDropped)=\"dropColumn($event)\">\n <th *ngIf=\"expandable\" class=\"mb-grid__select-cell mb-grid__header-cell\"></th>\n <th *ngIf=\"selectable\" class=\"mb-grid__select-cell mb-grid__header-cell\">\n <input\n type=\"checkbox\"\n [checked]=\"allVisibleRowsSelected\"\n [indeterminate]=\"someVisibleRowsSelected\"\n (change)=\"toggleAllVisible($event)\"\n [attr.aria-label]=\"messages.selectAllRows\" />\n </th>\n\n <th\n *ngFor=\"let column of visibleColumns; let columnIndex = index; trackBy: trackColumn\"\n cdkDrag\n class=\"mb-grid__header-cell\"\n [ngClass]=\"['mb-grid__cell--' + (column.headerAlign || column.align || 'start'), column.sortable === false ? '' : 'mb-grid__header-cell--sortable', getColumnClass(column)]\"\n [ngStyle]=\"getColumnStyle(column)\">\n <div class=\"mb-grid__header-content\" (click)=\"toggleSort(column, $event)\">\n <ng-container *ngIf=\"getHeaderTemplate(column) as headerTpl; else defaultHeader\">\n <ng-container [ngTemplateOutlet]=\"headerTpl\" [ngTemplateOutletContext]=\"{ $implicit: column, column: column }\"></ng-container>\n </ng-container>\n <ng-template #defaultHeader>\n <span class=\"mb-grid__header-title\">{{ column.title }}</span>\n </ng-template>\n\n <span *ngIf=\"getSortDirection(column) as dir\" class=\"mb-grid__sort\">\n <span>{{ dir === 'asc' ? '\u2191' : '\u2193' }}</span>\n <small *ngIf=\"multiSort && getSortIndex(column) >= 0\">{{ getSortIndex(column) + 1 }}</small>\n </span>\n </div>\n\n <button\n *ngIf=\"hasColumnMenu && column.menu !== false\"\n type=\"button\"\n class=\"mb-grid__column-menu-button\"\n [attr.aria-label]=\"messages.columnMenu\"\n (click)=\"toggleColumnMenu(column, $event)\">\u22EE</button>\n\n <span\n *ngIf=\"resizable && column.resizable !== false\"\n class=\"mb-grid__resize-handle\"\n (mousedown)=\"startResize(column, $event)\"></span>\n\n <div *ngIf=\"openColumnMenuField === column.field.toString()\" class=\"mb-grid__column-menu\" (click)=\"$event.stopPropagation()\">\n <button type=\"button\" class=\"mb-grid__menu-action\" (click)=\"toggleSort(column)\">{{ messages.sort }}</button>\n <button *ngIf=\"groupable && column.groupable !== false\" type=\"button\" class=\"mb-grid__menu-action\" (click)=\"toggleGroup(column)\">\n {{ state.group.length && state.group[0].field === column.field ? messages.removeGroup : messages.groupByColumn }}\n </button>\n\n <div *ngIf=\"filterable && column.filterable !== false && (filterMode === 'menu' || filterMode === 'both')\" class=\"mb-grid__menu-filter\">\n <label>{{ messages.filter }}</label>\n <select [ngModel]=\"filtersByField[column.field.toString()].operator\" (ngModelChange)=\"setFilterOperator(column, $event)\">\n <option *ngFor=\"let operator of getOperators(column)\" [ngValue]=\"operator\">{{ getOperatorLabel(operator) }}</option>\n </select>\n <input\n [type]=\"column.type === 'number' ? 'number' : column.type === 'date' ? 'date' : 'text'\"\n [ngModel]=\"filtersByField[column.field.toString()].value\"\n (ngModelChange)=\"setFilterValue(column, $event)\"\n [placeholder]=\"messages.filterValuePlaceholder\" />\n <button type=\"button\" class=\"mb-grid__menu-action\" (click)=\"clearFilter(column)\">{{ messages.clearFilter }}</button>\n </div>\n\n <button type=\"button\" class=\"mb-grid__menu-action\" (click)=\"toggleLock(column)\">{{ column.locked ? messages.unlockColumn : messages.lockColumn }}</button>\n <button type=\"button\" class=\"mb-grid__menu-action\" (click)=\"hideColumn(column)\">{{ messages.hideColumn }}</button>\n </div>\n </th>\n\n <th *ngIf=\"showCommandColumn\" class=\"mb-grid__header-cell mb-grid__command-cell\">{{ effectiveCommandColumnTitle }}</th>\n </tr>\n\n <tr *ngIf=\"hasFilterRow\" class=\"mb-grid__filter-row\">\n <th *ngIf=\"expandable\" class=\"mb-grid__select-cell\"></th>\n <th *ngIf=\"selectable\" class=\"mb-grid__select-cell\"></th>\n <th *ngFor=\"let column of visibleColumns; trackBy: trackColumn\" class=\"mb-grid__filter-cell\" [ngClass]=\"getColumnClass(column)\" [ngStyle]=\"getColumnStyle(column)\">\n <div *ngIf=\"column.filterable !== false\" class=\"mb-grid__filter-control\">\n <select\n class=\"mb-grid__filter-operator\"\n [ngModel]=\"filtersByField[column.field.toString()].operator\"\n (ngModelChange)=\"setFilterOperator(column, $event)\">\n <option *ngFor=\"let operator of getOperators(column)\" [ngValue]=\"operator\">{{ getOperatorLabel(operator) }}</option>\n </select>\n <input\n class=\"mb-grid__filter-input\"\n [type]=\"column.type === 'number' ? 'number' : column.type === 'date' ? 'date' : 'text'\"\n [placeholder]=\"column.title\"\n [ngModel]=\"filtersByField[column.field.toString()].value\"\n (ngModelChange)=\"setFilterValue(column, $event)\" />\n </div>\n </th>\n <th *ngIf=\"showCommandColumn\" class=\"mb-grid__command-cell\"></th>\n </tr>\n </thead>\n\n <tbody>\n <tr *ngIf=\"!processed.data.length && !loading\" class=\"mb-grid__empty-row\">\n <td class=\"mb-grid__empty\" [attr.colspan]=\"columnSpan\">{{ noRecordsText || messages.noRecords }}</td>\n </tr>\n\n <tr *ngIf=\"isNewRowEditing() && editingModel\" class=\"mb-grid__row mb-grid__row--editing mb-grid__row--new\">\n <td *ngIf=\"expandable\" class=\"mb-grid__select-cell\"></td>\n <td *ngIf=\"selectable\" class=\"mb-grid__select-cell\"></td>\n <td *ngFor=\"let column of visibleColumns; trackBy: trackColumn\" class=\"mb-grid__cell\">\n <ng-container [ngTemplateOutlet]=\"editorTemplate\" [ngTemplateOutletContext]=\"{ column: column, row: editingModel, rowIndex: editingRowIndex }\"></ng-container>\n </td>\n <td *ngIf=\"showCommandColumn\" class=\"mb-grid__cell mb-grid__command-cell\">\n <button type=\"button\" class=\"mb-grid__command\" (click)=\"saveEdit($event)\">{{ messages.save }}</button>\n <button type=\"button\" class=\"mb-grid__command\" (click)=\"cancelEditRow($event)\">{{ messages.cancel }}</button>\n </td>\n </tr>\n\n <ng-container *ngFor=\"let row of processed.data; trackBy: trackRow\">\n <tr *ngIf=\"row.kind === 'group'\" class=\"mb-grid__group-row\" (click)=\"toggleGroupRow(row)\">\n <td class=\"mb-grid__group-cell\" [attr.colspan]=\"columnSpan\">\n <span class=\"mb-grid__group-toggle\">{{ row.expanded ? '\u25BE' : '\u25B8' }}</span>\n <span [style.padding-inline-start.px]=\"row.level * 18\"></span>\n <strong>{{ row.field }}:</strong>\n <span>{{ row.value }}</span>\n <small>({{ row.count }})</small>\n </td>\n </tr>\n\n <tr\n *ngIf=\"row.kind === 'data'\"\n class=\"mb-grid__row\"\n [class.mb-grid__row--selected]=\"isSelected(row.dataItem)\"\n [class.mb-grid__row--editing]=\"isEditing(row.dataItem)\"\n [ngClass]=\"getRowClass(row.dataItem, row.rowIndex)\"\n (click)=\"onRowClick(row.dataItem, row.rowIndex)\">\n <td *ngIf=\"expandable\" class=\"mb-grid__select-cell\" (click)=\"$event.stopPropagation()\">\n <button type=\"button\" class=\"mb-grid__expand-button\" (click)=\"toggleDetail(row.dataItem, row.rowIndex, $event)\">{{ isDetailExpanded(row.dataItem) ? '\u25BE' : '\u25B8' }}</button>\n </td>\n <td *ngIf=\"selectable\" class=\"mb-grid__select-cell\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(row.dataItem)\"\n (change)=\"toggleRow(row.dataItem, $event)\"\n [attr.aria-label]=\"messages.selectRow\" />\n </td>\n\n <td\n *ngFor=\"let column of visibleColumns; trackBy: trackColumn\"\n class=\"mb-grid__cell\"\n [ngClass]=\"['mb-grid__cell--' + (column.align || 'start'), getCellClass(row.dataItem, column, row.rowIndex), getColumnClass(column)]\"\n [ngStyle]=\"getColumnStyle(column)\"\n (click)=\"onCellClick(row.dataItem, column, row.rowIndex)\">\n <ng-container *ngIf=\"isEditing(row.dataItem) && canEditColumn(column); else readonlyCell\">\n <ng-container [ngTemplateOutlet]=\"editorTemplate\" [ngTemplateOutletContext]=\"{ column: column, row: editingModel, rowIndex: row.rowIndex }\"></ng-container>\n </ng-container>\n <ng-template #readonlyCell>\n <ng-container *ngIf=\"getCellTemplate(column) as cellTpl; else defaultCell\">\n <ng-container\n [ngTemplateOutlet]=\"cellTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: row.dataItem, dataItem: row.dataItem, column: column, value: getValue(row.dataItem, column, row.rowIndex), rowIndex: row.rowIndex, editing: isEditing(row.dataItem) }\">\n </ng-container>\n </ng-container>\n <ng-template #defaultCell>{{ formatCell(row.dataItem, column, row.rowIndex) }}</ng-template>\n </ng-template>\n </td>\n\n <td *ngIf=\"showCommandColumn\" class=\"mb-grid__cell mb-grid__command-cell\" (click)=\"$event.stopPropagation()\">\n <ng-container *ngIf=\"isEditing(row.dataItem); else readonlyCommands\">\n <button type=\"button\" class=\"mb-grid__command\" (click)=\"saveEdit($event)\">{{ messages.save }}</button>\n <button type=\"button\" class=\"mb-grid__command\" (click)=\"cancelEditRow($event)\">{{ messages.cancel }}</button>\n </ng-container>\n <ng-template #readonlyCommands>\n <button *ngIf=\"editable\" type=\"button\" class=\"mb-grid__command\" (click)=\"startEdit(row.dataItem, row.rowIndex, $event)\">{{ messages.edit }}</button>\n <button *ngIf=\"allowDelete\" type=\"button\" class=\"mb-grid__command mb-grid__command--danger\" (click)=\"removeRow(row.dataItem, row.rowIndex, $event)\">{{ messages.delete }}</button>\n </ng-template>\n </td>\n </tr>\n\n <tr *ngIf=\"row.kind === 'data' && isDetailExpanded(row.dataItem) && detailTemplate?.templateRef as detailTpl\" class=\"mb-grid__detail-row\">\n <td [attr.colspan]=\"columnSpan\">\n <ng-container\n [ngTemplateOutlet]=\"detailTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: row.dataItem, dataItem: row.dataItem, rowIndex: row.rowIndex, expanded: isDetailExpanded(row.dataItem) }\">\n </ng-container>\n </td>\n </tr>\n </ng-container>\n </tbody>\n\n <tfoot *ngIf=\"showFooter\">\n <tr class=\"mb-grid__footer-row\">\n <td *ngIf=\"expandable\" class=\"mb-grid__select-cell\"></td>\n <td *ngIf=\"selectable\" class=\"mb-grid__select-cell\"></td>\n <td *ngFor=\"let column of visibleColumns; trackBy: trackColumn\" class=\"mb-grid__footer-cell\">\n <ng-container *ngIf=\"getFooterTemplate(column) as footerTpl; else defaultFooter\">\n <ng-container [ngTemplateOutlet]=\"footerTpl\" [ngTemplateOutletContext]=\"{ $implicit: column, column: column, aggregates: processed.aggregates }\"></ng-container>\n </ng-container>\n <ng-template #defaultFooter>\n <span *ngIf=\"processed.aggregates[column.field.toString()] as aggs\">\n <ng-container *ngFor=\"let key of aggs | keyvalue\">{{ key.key }}: {{ key.value | number }} </ng-container>\n </span>\n </ng-template>\n </td>\n <td *ngIf=\"showCommandColumn\" class=\"mb-grid__command-cell\"></td>\n </tr>\n </tfoot>\n </table>\n </div>\n\n\n <ng-template #editorTemplate let-column=\"column\" let-row=\"row\" let-rowIndex=\"rowIndex\">\n <ng-container *ngIf=\"column.editTemplate as editTpl; else defaultEditor\">\n <ng-container\n [ngTemplateOutlet]=\"editTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: row, dataItem: row, column: column, value: getEditValue(column), rowIndex: rowIndex, setValue: setEditValue.bind(this, column), errors: validationErrors }\">\n </ng-container>\n </ng-container>\n <ng-template #defaultEditor>\n <select\n *ngIf=\"getEditorType(column) === 'select'; else nonSelectEditor\"\n class=\"mb-grid__editor\"\n [ngModel]=\"getEditValue(column)\"\n (ngModelChange)=\"setEditValue(column, $event)\">\n <option *ngFor=\"let option of column.editorOptions || []\" [ngValue]=\"option.value\">{{ option.label }}</option>\n </select>\n <ng-template #nonSelectEditor>\n <input\n *ngIf=\"getEditorType(column) !== 'checkbox'; else checkboxEditor\"\n class=\"mb-grid__editor\"\n [type]=\"getEditorType(column) === 'number' ? 'number' : getEditorType(column) === 'date' ? 'date' : 'text'\"\n [ngModel]=\"getEditValue(column)\"\n (ngModelChange)=\"setEditValue(column, $event)\" />\n <ng-template #checkboxEditor>\n <input type=\"checkbox\" [ngModel]=\"getEditValue(column)\" (ngModelChange)=\"setEditValue(column, $event)\" />\n </ng-template>\n </ng-template>\n <small *ngIf=\"validationErrors[column.field.toString()]\" class=\"mb-grid__error\">{{ validationErrors[column.field.toString()] }}</small>\n </ng-template>\n </ng-template>\n\n <div *ngIf=\"pageable\" class=\"mb-grid__pager\">\n <div class=\"mb-grid__pager-info\">{{ pagerInfoText() }}</div>\n <div class=\"mb-grid__pager-actions\">\n <select class=\"mb-grid__page-size\" [ngModel]=\"pageSize\" (ngModelChange)=\"changePageSize($event)\">\n <option *ngFor=\"let option of pageSizeOptions\" [ngValue]=\"option\">{{ option }}</option>\n </select>\n <button type=\"button\" class=\"mb-grid__pager-button\" [disabled]=\"pageIndex === 0\" (click)=\"changePage(0)\">{{ messages.pagerFirst }}</button>\n <button type=\"button\" class=\"mb-grid__pager-button\" [disabled]=\"pageIndex === 0\" (click)=\"changePage(pageIndex - 1)\">{{ messages.pagerPrevious }}</button>\n <span class=\"mb-grid__page-current\">{{ pageIndex + 1 }} / {{ pageCount }}</span>\n <button type=\"button\" class=\"mb-grid__pager-button\" [disabled]=\"pageIndex >= pageCount - 1\" (click)=\"changePage(pageIndex + 1)\">{{ messages.pagerNext }}</button>\n <button type=\"button\" class=\"mb-grid__pager-button\" [disabled]=\"pageIndex >= pageCount - 1\" (click)=\"changePage(pageCount - 1)\">{{ messages.pagerLast }}</button>\n </div>\n </div>\n</div>\n", styles: [".mb-grid{position:relative;display:flex;flex-direction:column;width:100%;min-height:180px;overflow:hidden;border:1px solid var(--mb-border, #e2e8f0);border-radius:var(--mb-radius-lg, 14px);background:var(--mb-surface, #fff);color:var(--mb-text, #0f172a);box-shadow:var(--mb-shadow-sm, 0 1px 2px rgba(15, 23, 42, .06));font-family:var(--mb-font-family, inherit)}.mb-grid--compact{--mb-grid-row-height: 36px;--mb-grid-cell-padding-y: 8px;--mb-grid-cell-padding-x: 10px}.mb-grid--comfortable{--mb-grid-row-height: 44px;--mb-grid-cell-padding-y: 11px;--mb-grid-cell-padding-x: 14px}.mb-grid--spacious{--mb-grid-row-height: 54px;--mb-grid-cell-padding-y: 15px;--mb-grid-cell-padding-x: 18px}.mb-grid__toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px;border-block-end:1px solid var(--mb-border, #e2e8f0);background:color-mix(in srgb,var(--mb-surface, #fff) 88%,var(--mb-primary, #2563eb) 12%)}.mb-grid__toolbar-start,.mb-grid__toolbar-end{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.mb-grid__toolbar-end{margin-inline-start:auto}.mb-grid__group-panel{display:flex;align-items:center;gap:6px;flex-wrap:wrap;min-height:34px}.mb-grid__group-label,.mb-grid__group-hint{color:var(--mb-muted, #64748b);font-size:12px}.mb-grid__group-chip{border:1px solid var(--mb-primary, #2563eb);color:var(--mb-primary, #2563eb);background:#fff;padding:5px 10px;border-radius:999px;cursor:pointer}.mb-grid__search,.mb-grid__filter-input,.mb-grid__filter-operator,.mb-grid__page-size,.mb-grid__column-menu input,.mb-grid__column-menu select{min-height:34px;border:1px solid var(--mb-border, #e2e8f0);border-radius:var(--mb-radius-md, 10px);background:var(--mb-input-bg, #fff);color:var(--mb-text, #0f172a);padding:0 10px;outline:none;font:inherit}.mb-grid__search{min-width:220px}.mb-grid__search:focus,.mb-grid__filter-input:focus,.mb-grid__filter-operator:focus,.mb-grid__page-size:focus{border-color:var(--mb-primary, #2563eb);box-shadow:0 0 0 3px color-mix(in srgb,var(--mb-primary, #2563eb) 15%,transparent)}.mb-grid__tool-button,.mb-grid__pager-button,.mb-grid__menu-action{min-height:34px;border:1px solid var(--mb-border, #e2e8f0);border-radius:var(--mb-radius-md, 10px);background:var(--mb-surface, #fff);color:var(--mb-text, #0f172a);padding:0 12px;cursor:pointer;font:inherit;transition:background .15s ease,border-color .15s ease,transform .15s ease}.mb-grid__tool-button:hover,.mb-grid__pager-button:hover:not(:disabled),.mb-grid__menu-action:hover{border-color:color-mix(in srgb,var(--mb-primary, #2563eb) 45%,var(--mb-border, #e2e8f0));background:color-mix(in srgb,var(--mb-primary, #2563eb) 8%,#fff)}.mb-grid__pager-button:disabled{opacity:.45;cursor:not-allowed}.mb-grid__chooser{position:relative}.mb-grid__chooser-menu,.mb-grid__column-menu{position:absolute;z-index:20;inset-block-start:calc(100% + 6px);inset-inline-end:0;min-width:230px;padding:8px;border:1px solid var(--mb-border, #e2e8f0);border-radius:var(--mb-radius-lg, 14px);background:var(--mb-surface, #fff);box-shadow:var(--mb-shadow-lg, 0 16px 40px rgba(15, 23, 42, .16))}.mb-grid__chooser-option{display:flex;align-items:center;gap:8px;padding:8px;border-radius:var(--mb-radius-md, 10px);cursor:pointer}.mb-grid__chooser-option:hover{background:var(--mb-hover, #f8fafc)}.mb-grid__scroll{flex:1;min-height:0;overflow:auto}.mb-grid__table{width:100%;min-width:max-content;border-collapse:separate;border-spacing:0;table-layout:fixed}.mb-grid__header-row,.mb-grid__filter-row,.mb-grid__footer-row{background:var(--mb-grid-header-bg, #f8fafc)}.mb-grid__header-cell,.mb-grid__filter-cell,.mb-grid__footer-cell,.mb-grid__cell,.mb-grid__select-cell{position:relative;border-block-end:1px solid var(--mb-border, #e2e8f0);border-inline-end:1px solid var(--mb-border-subtle, #eef2f7);padding:var(--mb-grid-cell-padding-y) var(--mb-grid-cell-padding-x);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:middle;height:var(--mb-grid-row-height)}.mb-grid__cell--locked-start,.mb-grid__header-cell.mb-grid__cell--locked-start,.mb-grid__filter-cell.mb-grid__cell--locked-start,.mb-grid__footer-cell.mb-grid__cell--locked-start{background:var(--mb-grid-locked-bg, var(--mb-surface, #fff));box-shadow:4px 0 8px -4px #0f172a1f}.mb-grid__cell--locked-end,.mb-grid__header-cell.mb-grid__cell--locked-end,.mb-grid__filter-cell.mb-grid__cell--locked-end,.mb-grid__footer-cell.mb-grid__cell--locked-end{background:var(--mb-grid-locked-bg, var(--mb-surface, #fff));box-shadow:-4px 0 8px -4px #0f172a1f}.mb-grid__header-cell{position:sticky;inset-block-start:0;z-index:5;-webkit-user-select:none;user-select:none;font-weight:700;color:var(--mb-grid-header-text, #334155);background:var(--mb-grid-header-bg, #f8fafc)}.mb-grid__filter-cell{position:sticky;inset-block-start:var(--mb-grid-row-height);z-index:4;background:var(--mb-grid-header-bg, #f8fafc);padding:8px}.mb-grid__header-cell--sortable .mb-grid__header-content{cursor:pointer}.mb-grid__header-content{display:flex;align-items:center;gap:6px;min-width:0}.mb-grid__header-title{overflow:hidden;text-overflow:ellipsis}.mb-grid__sort{display:inline-flex;align-items:center;gap:2px;color:var(--mb-primary, #2563eb)}.mb-grid__sort small{font-size:10px}.mb-grid__column-menu-button{position:absolute;inset-inline-end:4px;inset-block-start:50%;transform:translateY(-50%);width:26px;height:26px;border:none;border-radius:8px;background:transparent;cursor:pointer;opacity:.55}.mb-grid__column-menu-button:hover{background:var(--mb-hover, #f1f5f9);opacity:1}.mb-grid__resize-handle{position:absolute;inset-block:8px;inset-inline-end:-2px;width:5px;cursor:col-resize;z-index:7}.mb-grid__resize-handle:hover{background:color-mix(in srgb,var(--mb-primary, #2563eb) 40%,transparent)}.mb-grid__column-menu{inset-block-start:calc(100% - 4px);inset-inline-end:6px;text-align:start}.mb-grid__menu-action{display:flex;align-items:center;width:100%;justify-content:flex-start;margin-block:4px}.mb-grid__menu-filter{display:grid;gap:8px;margin-block:8px;padding-block:8px;border-block:1px solid var(--mb-border, #e2e8f0)}.mb-grid__filter-control{display:grid;grid-template-columns:minmax(105px,.75fr) minmax(120px,1fr);gap:6px}.mb-grid__row{background:var(--mb-surface, #fff);transition:background .12s ease}.mb-grid__row:hover{background:var(--mb-hover, #f8fafc)}.mb-grid__row--selected{background:color-mix(in srgb,var(--mb-primary, #2563eb) 10%,#fff)}.mb-grid__cell--start{text-align:start}.mb-grid__cell--center{text-align:center}.mb-grid__cell--end{text-align:end}.mb-grid__select-cell{width:48px;min-width:48px;max-width:48px;text-align:center}.mb-grid__group-row{background:color-mix(in srgb,var(--mb-primary, #2563eb) 8%,#fff);cursor:pointer}.mb-grid__group-cell{padding:11px 14px;border-block-end:1px solid var(--mb-border, #e2e8f0);font-weight:600}.mb-grid__group-toggle{display:inline-flex;width:20px;justify-content:center;color:var(--mb-primary, #2563eb)}.mb-grid__detail-row td{padding:0;border-block-end:1px solid var(--mb-border, #e2e8f0);background:var(--mb-subtle, #f8fafc)}.mb-grid__empty{text-align:center;padding:36px 16px;color:var(--mb-muted, #64748b)}.mb-grid__footer-cell{font-weight:700;background:var(--mb-grid-footer-bg, #f8fafc)}.mb-grid__pager{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border-block-start:1px solid var(--mb-border, #e2e8f0);background:var(--mb-surface, #fff)}.mb-grid__pager-actions{display:flex;align-items:center;gap:6px;flex-wrap:wrap}.mb-grid__pager-info,.mb-grid__page-current{color:var(--mb-muted, #64748b);font-size:13px}.mb-grid__loading{position:absolute;z-index:30;inset:0;display:flex;align-items:center;justify-content:center;gap:10px;background:#ffffffad;backdrop-filter:blur(2px)}.mb-grid__spinner{width:24px;height:24px;border-radius:50%;border:3px solid var(--mb-border, #e2e8f0);border-block-start-color:var(--mb-primary, #2563eb);animation:mb-grid-spin .8s linear infinite}.cdk-drag-preview{box-sizing:border-box;border-radius:var(--mb-radius-md, 10px);box-shadow:var(--mb-shadow-lg, 0 16px 40px rgba(15, 23, 42, .16));background:#fff;padding:10px 14px}.cdk-drag-placeholder{opacity:.25}@keyframes mb-grid-spin{to{transform:rotate(360deg)}}@media (max-width: 720px){.mb-grid__toolbar,.mb-grid__pager{align-items:stretch;flex-direction:column}.mb-grid__toolbar-end,.mb-grid__pager-actions{width:100%}.mb-grid__search{width:100%;min-width:0}}.mb-grid__tool-button--primary{background:var(--mb-primary, #2563eb);border-color:var(--mb-primary, #2563eb);color:#fff}.mb-grid__command-cell{width:1%;white-space:nowrap;text-align:center}.mb-grid__command{min-height:30px;margin-inline:2px;padding:0 10px;border:1px solid var(--mb-border, #e2e8f0);border-radius:var(--mb-radius-sm, 8px);background:var(--mb-surface, #fff);color:var(--mb-text, #0f172a);cursor:pointer;font:inherit}.mb-grid__command:hover{border-color:var(--mb-primary, #2563eb);color:var(--mb-primary, #2563eb)}.mb-grid__command--danger:hover{border-color:var(--mb-danger, #dc2626);color:var(--mb-danger, #dc2626)}.mb-grid__row--editing{background:color-mix(in srgb,var(--mb-primary, #2563eb) 7%,var(--mb-surface, #fff))}.mb-grid__row--new{border-block-start:2px solid var(--mb-primary, #2563eb)}.mb-grid__editor{width:100%;min-height:34px;border:1px solid var(--mb-border, #e2e8f0);border-radius:var(--mb-radius-sm, 8px);background:var(--mb-input-bg, #fff);color:var(--mb-text, #0f172a);padding-inline:8px;font:inherit;outline:none}.mb-grid__editor:focus{border-color:var(--mb-primary, #2563eb);box-shadow:0 0 0 3px color-mix(in srgb,var(--mb-primary, #2563eb) 15%,transparent)}.mb-grid__error{display:block;margin-block-start:4px;color:var(--mb-danger, #dc2626);font-size:11px}.mb-grid__expand-button{border:0;background:transparent;cursor:pointer;font-size:16px;color:var(--mb-muted, #64748b)}.mb-grid__detail-row td{padding:14px;background:color-mix(in srgb,var(--mb-surface, #fff) 92%,var(--mb-primary, #2563eb) 8%)}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: DecimalPipe, name: "number" }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i3.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i3.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "ngmodule", type: ScrollingModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2024
+ }
2025
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbGridComponent, decorators: [{
2026
+ type: Component,
2027
+ args: [{ selector: 'mb-grid', standalone: true, imports: [NgIf, NgFor, NgClass, NgStyle, NgTemplateOutlet, DecimalPipe, KeyValuePipe, FormsModule, DragDropModule, ScrollingModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mb-grid\" [attr.dir]=\"effectiveDir\" [ngClass]=\"['mb-grid--' + density, loading ? 'mb-grid--loading' : '']\" [ngStyle]=\"gridStyle\">\n <div *ngIf=\"showToolbar\" class=\"mb-grid__toolbar\">\n <ng-container *ngIf=\"toolbarTemplate?.templateRef as toolbarTpl; else defaultToolbar\">\n <ng-container [ngTemplateOutlet]=\"toolbarTpl\" [ngTemplateOutletContext]=\"toolbarContext\"></ng-container>\n </ng-container>\n\n <ng-template #defaultToolbar>\n <div class=\"mb-grid__toolbar-start\">\n <div *ngIf=\"groupable\" class=\"mb-grid__group-panel\">\n <span class=\"mb-grid__group-label\">{{ messages.groupLabel }}</span>\n <button\n *ngFor=\"let group of state.group\"\n type=\"button\"\n class=\"mb-grid__group-chip\"\n (click)=\"removeGroup(group)\">\n {{ group.field }} \u00D7\n </button>\n <span *ngIf=\"!state.group.length\" class=\"mb-grid__group-hint\">{{ messages.groupHint }}</span>\n </div>\n </div>\n\n <div class=\"mb-grid__toolbar-end\">\n <button *ngIf=\"editable && allowAdd\" type=\"button\" class=\"mb-grid__tool-button mb-grid__tool-button--primary\" (click)=\"createRow()\">{{ messages.addRow }}</button>\n <input\n *ngIf=\"showSearch\"\n class=\"mb-grid__search\"\n [ngModel]=\"globalSearch\"\n (ngModelChange)=\"applyGlobalSearch($event)\"\n [placeholder]=\"messages.searchPlaceholder\" />\n\n <div *ngIf=\"showColumnChooser\" class=\"mb-grid__chooser\">\n <button type=\"button\" class=\"mb-grid__tool-button\" (click)=\"openColumnMenuField = openColumnMenuField === '__chooser' ? '' : '__chooser'\">\n {{ messages.columns }}\n </button>\n <div *ngIf=\"openColumnMenuField === '__chooser'\" class=\"mb-grid__chooser-menu\" (click)=\"$event.stopPropagation()\">\n <label *ngFor=\"let column of internalColumns\" class=\"mb-grid__chooser-option\">\n <input type=\"checkbox\" [checked]=\"!column.hidden\" (change)=\"showColumn(column, $any($event.target).checked)\" />\n <span>{{ column.title }}</span>\n </label>\n <button type=\"button\" class=\"mb-grid__menu-action\" (click)=\"resetColumns()\">{{ messages.resetColumns }}</button>\n </div>\n </div>\n\n <button *ngIf=\"showExport\" type=\"button\" class=\"mb-grid__tool-button\" (click)=\"exportCsv()\">{{ messages.exportCsv }}</button>\n <button *ngIf=\"showExport && showExcelExport\" type=\"button\" class=\"mb-grid__tool-button\" (click)=\"exportExcel()\">{{ messages.exportExcel }}</button>\n <button *ngIf=\"showExport && showJsonExport\" type=\"button\" class=\"mb-grid__tool-button\" (click)=\"exportJson()\">{{ messages.exportJson }}</button>\n <button type=\"button\" class=\"mb-grid__tool-button\" (click)=\"resetState()\">{{ messages.reset }}</button>\n </div>\n </ng-template>\n </div>\n\n <div *ngIf=\"loading\" class=\"mb-grid__loading\" aria-live=\"polite\">\n <span class=\"mb-grid__spinner\"></span>\n <span>{{ loadingText || messages.loading }}</span>\n </div>\n\n <div class=\"mb-grid__scroll\">\n <table class=\"mb-grid__table\">\n <thead>\n <tr\n class=\"mb-grid__header-row\"\n cdkDropList\n cdkDropListOrientation=\"horizontal\"\n [cdkDropListDisabled]=\"!reorderable\"\n [cdkDropListData]=\"visibleColumns\"\n (cdkDropListDropped)=\"dropColumn($event)\">\n <th *ngIf=\"expandable\" class=\"mb-grid__select-cell mb-grid__header-cell\"></th>\n <th *ngIf=\"selectable\" class=\"mb-grid__select-cell mb-grid__header-cell\">\n <input\n type=\"checkbox\"\n [checked]=\"allVisibleRowsSelected\"\n [indeterminate]=\"someVisibleRowsSelected\"\n (change)=\"toggleAllVisible($event)\"\n [attr.aria-label]=\"messages.selectAllRows\" />\n </th>\n\n <th\n *ngFor=\"let column of visibleColumns; let columnIndex = index; trackBy: trackColumn\"\n cdkDrag\n class=\"mb-grid__header-cell\"\n [ngClass]=\"['mb-grid__cell--' + (column.headerAlign || column.align || 'start'), column.sortable === false ? '' : 'mb-grid__header-cell--sortable', getColumnClass(column)]\"\n [ngStyle]=\"getColumnStyle(column)\">\n <div class=\"mb-grid__header-content\" (click)=\"toggleSort(column, $event)\">\n <ng-container *ngIf=\"getHeaderTemplate(column) as headerTpl; else defaultHeader\">\n <ng-container [ngTemplateOutlet]=\"headerTpl\" [ngTemplateOutletContext]=\"{ $implicit: column, column: column }\"></ng-container>\n </ng-container>\n <ng-template #defaultHeader>\n <span class=\"mb-grid__header-title\">{{ column.title }}</span>\n </ng-template>\n\n <span *ngIf=\"getSortDirection(column) as dir\" class=\"mb-grid__sort\">\n <span>{{ dir === 'asc' ? '\u2191' : '\u2193' }}</span>\n <small *ngIf=\"multiSort && getSortIndex(column) >= 0\">{{ getSortIndex(column) + 1 }}</small>\n </span>\n </div>\n\n <button\n *ngIf=\"hasColumnMenu && column.menu !== false\"\n type=\"button\"\n class=\"mb-grid__column-menu-button\"\n [attr.aria-label]=\"messages.columnMenu\"\n (click)=\"toggleColumnMenu(column, $event)\">\u22EE</button>\n\n <span\n *ngIf=\"resizable && column.resizable !== false\"\n class=\"mb-grid__resize-handle\"\n (mousedown)=\"startResize(column, $event)\"></span>\n\n <div *ngIf=\"openColumnMenuField === column.field.toString()\" class=\"mb-grid__column-menu\" (click)=\"$event.stopPropagation()\">\n <button type=\"button\" class=\"mb-grid__menu-action\" (click)=\"toggleSort(column)\">{{ messages.sort }}</button>\n <button *ngIf=\"groupable && column.groupable !== false\" type=\"button\" class=\"mb-grid__menu-action\" (click)=\"toggleGroup(column)\">\n {{ state.group.length && state.group[0].field === column.field ? messages.removeGroup : messages.groupByColumn }}\n </button>\n\n <div *ngIf=\"filterable && column.filterable !== false && (filterMode === 'menu' || filterMode === 'both')\" class=\"mb-grid__menu-filter\">\n <label>{{ messages.filter }}</label>\n <select [ngModel]=\"filtersByField[column.field.toString()].operator\" (ngModelChange)=\"setFilterOperator(column, $event)\">\n <option *ngFor=\"let operator of getOperators(column)\" [ngValue]=\"operator\">{{ getOperatorLabel(operator) }}</option>\n </select>\n <input\n [type]=\"column.type === 'number' ? 'number' : column.type === 'date' ? 'date' : 'text'\"\n [ngModel]=\"filtersByField[column.field.toString()].value\"\n (ngModelChange)=\"setFilterValue(column, $event)\"\n [placeholder]=\"messages.filterValuePlaceholder\" />\n <button type=\"button\" class=\"mb-grid__menu-action\" (click)=\"clearFilter(column)\">{{ messages.clearFilter }}</button>\n </div>\n\n <button type=\"button\" class=\"mb-grid__menu-action\" (click)=\"toggleLock(column)\">{{ column.locked ? messages.unlockColumn : messages.lockColumn }}</button>\n <button type=\"button\" class=\"mb-grid__menu-action\" (click)=\"hideColumn(column)\">{{ messages.hideColumn }}</button>\n </div>\n </th>\n\n <th *ngIf=\"showCommandColumn\" class=\"mb-grid__header-cell mb-grid__command-cell\">{{ effectiveCommandColumnTitle }}</th>\n </tr>\n\n <tr *ngIf=\"hasFilterRow\" class=\"mb-grid__filter-row\">\n <th *ngIf=\"expandable\" class=\"mb-grid__select-cell\"></th>\n <th *ngIf=\"selectable\" class=\"mb-grid__select-cell\"></th>\n <th *ngFor=\"let column of visibleColumns; trackBy: trackColumn\" class=\"mb-grid__filter-cell\" [ngClass]=\"getColumnClass(column)\" [ngStyle]=\"getColumnStyle(column)\">\n <div *ngIf=\"column.filterable !== false\" class=\"mb-grid__filter-control\">\n <select\n class=\"mb-grid__filter-operator\"\n [ngModel]=\"filtersByField[column.field.toString()].operator\"\n (ngModelChange)=\"setFilterOperator(column, $event)\">\n <option *ngFor=\"let operator of getOperators(column)\" [ngValue]=\"operator\">{{ getOperatorLabel(operator) }}</option>\n </select>\n <input\n class=\"mb-grid__filter-input\"\n [type]=\"column.type === 'number' ? 'number' : column.type === 'date' ? 'date' : 'text'\"\n [placeholder]=\"column.title\"\n [ngModel]=\"filtersByField[column.field.toString()].value\"\n (ngModelChange)=\"setFilterValue(column, $event)\" />\n </div>\n </th>\n <th *ngIf=\"showCommandColumn\" class=\"mb-grid__command-cell\"></th>\n </tr>\n </thead>\n\n <tbody>\n <tr *ngIf=\"!processed.data.length && !loading\" class=\"mb-grid__empty-row\">\n <td class=\"mb-grid__empty\" [attr.colspan]=\"columnSpan\">{{ noRecordsText || messages.noRecords }}</td>\n </tr>\n\n <tr *ngIf=\"isNewRowEditing() && editingModel\" class=\"mb-grid__row mb-grid__row--editing mb-grid__row--new\">\n <td *ngIf=\"expandable\" class=\"mb-grid__select-cell\"></td>\n <td *ngIf=\"selectable\" class=\"mb-grid__select-cell\"></td>\n <td *ngFor=\"let column of visibleColumns; trackBy: trackColumn\" class=\"mb-grid__cell\">\n <ng-container [ngTemplateOutlet]=\"editorTemplate\" [ngTemplateOutletContext]=\"{ column: column, row: editingModel, rowIndex: editingRowIndex }\"></ng-container>\n </td>\n <td *ngIf=\"showCommandColumn\" class=\"mb-grid__cell mb-grid__command-cell\">\n <button type=\"button\" class=\"mb-grid__command\" (click)=\"saveEdit($event)\">{{ messages.save }}</button>\n <button type=\"button\" class=\"mb-grid__command\" (click)=\"cancelEditRow($event)\">{{ messages.cancel }}</button>\n </td>\n </tr>\n\n <ng-container *ngFor=\"let row of processed.data; trackBy: trackRow\">\n <tr *ngIf=\"row.kind === 'group'\" class=\"mb-grid__group-row\" (click)=\"toggleGroupRow(row)\">\n <td class=\"mb-grid__group-cell\" [attr.colspan]=\"columnSpan\">\n <span class=\"mb-grid__group-toggle\">{{ row.expanded ? '\u25BE' : '\u25B8' }}</span>\n <span [style.padding-inline-start.px]=\"row.level * 18\"></span>\n <strong>{{ row.field }}:</strong>\n <span>{{ row.value }}</span>\n <small>({{ row.count }})</small>\n </td>\n </tr>\n\n <tr\n *ngIf=\"row.kind === 'data'\"\n class=\"mb-grid__row\"\n [class.mb-grid__row--selected]=\"isSelected(row.dataItem)\"\n [class.mb-grid__row--editing]=\"isEditing(row.dataItem)\"\n [ngClass]=\"getRowClass(row.dataItem, row.rowIndex)\"\n (click)=\"onRowClick(row.dataItem, row.rowIndex)\">\n <td *ngIf=\"expandable\" class=\"mb-grid__select-cell\" (click)=\"$event.stopPropagation()\">\n <button type=\"button\" class=\"mb-grid__expand-button\" (click)=\"toggleDetail(row.dataItem, row.rowIndex, $event)\">{{ isDetailExpanded(row.dataItem) ? '\u25BE' : '\u25B8' }}</button>\n </td>\n <td *ngIf=\"selectable\" class=\"mb-grid__select-cell\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(row.dataItem)\"\n (change)=\"toggleRow(row.dataItem, $event)\"\n [attr.aria-label]=\"messages.selectRow\" />\n </td>\n\n <td\n *ngFor=\"let column of visibleColumns; trackBy: trackColumn\"\n class=\"mb-grid__cell\"\n [ngClass]=\"['mb-grid__cell--' + (column.align || 'start'), getCellClass(row.dataItem, column, row.rowIndex), getColumnClass(column)]\"\n [ngStyle]=\"getColumnStyle(column)\"\n (click)=\"onCellClick(row.dataItem, column, row.rowIndex)\">\n <ng-container *ngIf=\"isEditing(row.dataItem) && canEditColumn(column); else readonlyCell\">\n <ng-container [ngTemplateOutlet]=\"editorTemplate\" [ngTemplateOutletContext]=\"{ column: column, row: editingModel, rowIndex: row.rowIndex }\"></ng-container>\n </ng-container>\n <ng-template #readonlyCell>\n <ng-container *ngIf=\"getCellTemplate(column) as cellTpl; else defaultCell\">\n <ng-container\n [ngTemplateOutlet]=\"cellTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: row.dataItem, dataItem: row.dataItem, column: column, value: getValue(row.dataItem, column, row.rowIndex), rowIndex: row.rowIndex, editing: isEditing(row.dataItem) }\">\n </ng-container>\n </ng-container>\n <ng-template #defaultCell>{{ formatCell(row.dataItem, column, row.rowIndex) }}</ng-template>\n </ng-template>\n </td>\n\n <td *ngIf=\"showCommandColumn\" class=\"mb-grid__cell mb-grid__command-cell\" (click)=\"$event.stopPropagation()\">\n <ng-container *ngIf=\"isEditing(row.dataItem); else readonlyCommands\">\n <button type=\"button\" class=\"mb-grid__command\" (click)=\"saveEdit($event)\">{{ messages.save }}</button>\n <button type=\"button\" class=\"mb-grid__command\" (click)=\"cancelEditRow($event)\">{{ messages.cancel }}</button>\n </ng-container>\n <ng-template #readonlyCommands>\n <button *ngIf=\"editable\" type=\"button\" class=\"mb-grid__command\" (click)=\"startEdit(row.dataItem, row.rowIndex, $event)\">{{ messages.edit }}</button>\n <button *ngIf=\"allowDelete\" type=\"button\" class=\"mb-grid__command mb-grid__command--danger\" (click)=\"removeRow(row.dataItem, row.rowIndex, $event)\">{{ messages.delete }}</button>\n </ng-template>\n </td>\n </tr>\n\n <tr *ngIf=\"row.kind === 'data' && isDetailExpanded(row.dataItem) && detailTemplate?.templateRef as detailTpl\" class=\"mb-grid__detail-row\">\n <td [attr.colspan]=\"columnSpan\">\n <ng-container\n [ngTemplateOutlet]=\"detailTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: row.dataItem, dataItem: row.dataItem, rowIndex: row.rowIndex, expanded: isDetailExpanded(row.dataItem) }\">\n </ng-container>\n </td>\n </tr>\n </ng-container>\n </tbody>\n\n <tfoot *ngIf=\"showFooter\">\n <tr class=\"mb-grid__footer-row\">\n <td *ngIf=\"expandable\" class=\"mb-grid__select-cell\"></td>\n <td *ngIf=\"selectable\" class=\"mb-grid__select-cell\"></td>\n <td *ngFor=\"let column of visibleColumns; trackBy: trackColumn\" class=\"mb-grid__footer-cell\">\n <ng-container *ngIf=\"getFooterTemplate(column) as footerTpl; else defaultFooter\">\n <ng-container [ngTemplateOutlet]=\"footerTpl\" [ngTemplateOutletContext]=\"{ $implicit: column, column: column, aggregates: processed.aggregates }\"></ng-container>\n </ng-container>\n <ng-template #defaultFooter>\n <span *ngIf=\"processed.aggregates[column.field.toString()] as aggs\">\n <ng-container *ngFor=\"let key of aggs | keyvalue\">{{ key.key }}: {{ key.value | number }} </ng-container>\n </span>\n </ng-template>\n </td>\n <td *ngIf=\"showCommandColumn\" class=\"mb-grid__command-cell\"></td>\n </tr>\n </tfoot>\n </table>\n </div>\n\n\n <ng-template #editorTemplate let-column=\"column\" let-row=\"row\" let-rowIndex=\"rowIndex\">\n <ng-container *ngIf=\"column.editTemplate as editTpl; else defaultEditor\">\n <ng-container\n [ngTemplateOutlet]=\"editTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: row, dataItem: row, column: column, value: getEditValue(column), rowIndex: rowIndex, setValue: setEditValue.bind(this, column), errors: validationErrors }\">\n </ng-container>\n </ng-container>\n <ng-template #defaultEditor>\n <select\n *ngIf=\"getEditorType(column) === 'select'; else nonSelectEditor\"\n class=\"mb-grid__editor\"\n [ngModel]=\"getEditValue(column)\"\n (ngModelChange)=\"setEditValue(column, $event)\">\n <option *ngFor=\"let option of column.editorOptions || []\" [ngValue]=\"option.value\">{{ option.label }}</option>\n </select>\n <ng-template #nonSelectEditor>\n <input\n *ngIf=\"getEditorType(column) !== 'checkbox'; else checkboxEditor\"\n class=\"mb-grid__editor\"\n [type]=\"getEditorType(column) === 'number' ? 'number' : getEditorType(column) === 'date' ? 'date' : 'text'\"\n [ngModel]=\"getEditValue(column)\"\n (ngModelChange)=\"setEditValue(column, $event)\" />\n <ng-template #checkboxEditor>\n <input type=\"checkbox\" [ngModel]=\"getEditValue(column)\" (ngModelChange)=\"setEditValue(column, $event)\" />\n </ng-template>\n </ng-template>\n <small *ngIf=\"validationErrors[column.field.toString()]\" class=\"mb-grid__error\">{{ validationErrors[column.field.toString()] }}</small>\n </ng-template>\n </ng-template>\n\n <div *ngIf=\"pageable\" class=\"mb-grid__pager\">\n <div class=\"mb-grid__pager-info\">{{ pagerInfoText() }}</div>\n <div class=\"mb-grid__pager-actions\">\n <select class=\"mb-grid__page-size\" [ngModel]=\"pageSize\" (ngModelChange)=\"changePageSize($event)\">\n <option *ngFor=\"let option of pageSizeOptions\" [ngValue]=\"option\">{{ option }}</option>\n </select>\n <button type=\"button\" class=\"mb-grid__pager-button\" [disabled]=\"pageIndex === 0\" (click)=\"changePage(0)\">{{ messages.pagerFirst }}</button>\n <button type=\"button\" class=\"mb-grid__pager-button\" [disabled]=\"pageIndex === 0\" (click)=\"changePage(pageIndex - 1)\">{{ messages.pagerPrevious }}</button>\n <span class=\"mb-grid__page-current\">{{ pageIndex + 1 }} / {{ pageCount }}</span>\n <button type=\"button\" class=\"mb-grid__pager-button\" [disabled]=\"pageIndex >= pageCount - 1\" (click)=\"changePage(pageIndex + 1)\">{{ messages.pagerNext }}</button>\n <button type=\"button\" class=\"mb-grid__pager-button\" [disabled]=\"pageIndex >= pageCount - 1\" (click)=\"changePage(pageCount - 1)\">{{ messages.pagerLast }}</button>\n </div>\n </div>\n</div>\n", styles: [".mb-grid{position:relative;display:flex;flex-direction:column;width:100%;min-height:180px;overflow:hidden;border:1px solid var(--mb-border, #e2e8f0);border-radius:var(--mb-radius-lg, 14px);background:var(--mb-surface, #fff);color:var(--mb-text, #0f172a);box-shadow:var(--mb-shadow-sm, 0 1px 2px rgba(15, 23, 42, .06));font-family:var(--mb-font-family, inherit)}.mb-grid--compact{--mb-grid-row-height: 36px;--mb-grid-cell-padding-y: 8px;--mb-grid-cell-padding-x: 10px}.mb-grid--comfortable{--mb-grid-row-height: 44px;--mb-grid-cell-padding-y: 11px;--mb-grid-cell-padding-x: 14px}.mb-grid--spacious{--mb-grid-row-height: 54px;--mb-grid-cell-padding-y: 15px;--mb-grid-cell-padding-x: 18px}.mb-grid__toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px;border-block-end:1px solid var(--mb-border, #e2e8f0);background:color-mix(in srgb,var(--mb-surface, #fff) 88%,var(--mb-primary, #2563eb) 12%)}.mb-grid__toolbar-start,.mb-grid__toolbar-end{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.mb-grid__toolbar-end{margin-inline-start:auto}.mb-grid__group-panel{display:flex;align-items:center;gap:6px;flex-wrap:wrap;min-height:34px}.mb-grid__group-label,.mb-grid__group-hint{color:var(--mb-muted, #64748b);font-size:12px}.mb-grid__group-chip{border:1px solid var(--mb-primary, #2563eb);color:var(--mb-primary, #2563eb);background:#fff;padding:5px 10px;border-radius:999px;cursor:pointer}.mb-grid__search,.mb-grid__filter-input,.mb-grid__filter-operator,.mb-grid__page-size,.mb-grid__column-menu input,.mb-grid__column-menu select{min-height:34px;border:1px solid var(--mb-border, #e2e8f0);border-radius:var(--mb-radius-md, 10px);background:var(--mb-input-bg, #fff);color:var(--mb-text, #0f172a);padding:0 10px;outline:none;font:inherit}.mb-grid__search{min-width:220px}.mb-grid__search:focus,.mb-grid__filter-input:focus,.mb-grid__filter-operator:focus,.mb-grid__page-size:focus{border-color:var(--mb-primary, #2563eb);box-shadow:0 0 0 3px color-mix(in srgb,var(--mb-primary, #2563eb) 15%,transparent)}.mb-grid__tool-button,.mb-grid__pager-button,.mb-grid__menu-action{min-height:34px;border:1px solid var(--mb-border, #e2e8f0);border-radius:var(--mb-radius-md, 10px);background:var(--mb-surface, #fff);color:var(--mb-text, #0f172a);padding:0 12px;cursor:pointer;font:inherit;transition:background .15s ease,border-color .15s ease,transform .15s ease}.mb-grid__tool-button:hover,.mb-grid__pager-button:hover:not(:disabled),.mb-grid__menu-action:hover{border-color:color-mix(in srgb,var(--mb-primary, #2563eb) 45%,var(--mb-border, #e2e8f0));background:color-mix(in srgb,var(--mb-primary, #2563eb) 8%,#fff)}.mb-grid__pager-button:disabled{opacity:.45;cursor:not-allowed}.mb-grid__chooser{position:relative}.mb-grid__chooser-menu,.mb-grid__column-menu{position:absolute;z-index:20;inset-block-start:calc(100% + 6px);inset-inline-end:0;min-width:230px;padding:8px;border:1px solid var(--mb-border, #e2e8f0);border-radius:var(--mb-radius-lg, 14px);background:var(--mb-surface, #fff);box-shadow:var(--mb-shadow-lg, 0 16px 40px rgba(15, 23, 42, .16))}.mb-grid__chooser-option{display:flex;align-items:center;gap:8px;padding:8px;border-radius:var(--mb-radius-md, 10px);cursor:pointer}.mb-grid__chooser-option:hover{background:var(--mb-hover, #f8fafc)}.mb-grid__scroll{flex:1;min-height:0;overflow:auto}.mb-grid__table{width:100%;min-width:max-content;border-collapse:separate;border-spacing:0;table-layout:fixed}.mb-grid__header-row,.mb-grid__filter-row,.mb-grid__footer-row{background:var(--mb-grid-header-bg, #f8fafc)}.mb-grid__header-cell,.mb-grid__filter-cell,.mb-grid__footer-cell,.mb-grid__cell,.mb-grid__select-cell{position:relative;border-block-end:1px solid var(--mb-border, #e2e8f0);border-inline-end:1px solid var(--mb-border-subtle, #eef2f7);padding:var(--mb-grid-cell-padding-y) var(--mb-grid-cell-padding-x);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:middle;height:var(--mb-grid-row-height)}.mb-grid__cell--locked-start,.mb-grid__header-cell.mb-grid__cell--locked-start,.mb-grid__filter-cell.mb-grid__cell--locked-start,.mb-grid__footer-cell.mb-grid__cell--locked-start{background:var(--mb-grid-locked-bg, var(--mb-surface, #fff));box-shadow:4px 0 8px -4px #0f172a1f}.mb-grid__cell--locked-end,.mb-grid__header-cell.mb-grid__cell--locked-end,.mb-grid__filter-cell.mb-grid__cell--locked-end,.mb-grid__footer-cell.mb-grid__cell--locked-end{background:var(--mb-grid-locked-bg, var(--mb-surface, #fff));box-shadow:-4px 0 8px -4px #0f172a1f}.mb-grid__header-cell{position:sticky;inset-block-start:0;z-index:5;-webkit-user-select:none;user-select:none;font-weight:700;color:var(--mb-grid-header-text, #334155);background:var(--mb-grid-header-bg, #f8fafc)}.mb-grid__filter-cell{position:sticky;inset-block-start:var(--mb-grid-row-height);z-index:4;background:var(--mb-grid-header-bg, #f8fafc);padding:8px}.mb-grid__header-cell--sortable .mb-grid__header-content{cursor:pointer}.mb-grid__header-content{display:flex;align-items:center;gap:6px;min-width:0}.mb-grid__header-title{overflow:hidden;text-overflow:ellipsis}.mb-grid__sort{display:inline-flex;align-items:center;gap:2px;color:var(--mb-primary, #2563eb)}.mb-grid__sort small{font-size:10px}.mb-grid__column-menu-button{position:absolute;inset-inline-end:4px;inset-block-start:50%;transform:translateY(-50%);width:26px;height:26px;border:none;border-radius:8px;background:transparent;cursor:pointer;opacity:.55}.mb-grid__column-menu-button:hover{background:var(--mb-hover, #f1f5f9);opacity:1}.mb-grid__resize-handle{position:absolute;inset-block:8px;inset-inline-end:-2px;width:5px;cursor:col-resize;z-index:7}.mb-grid__resize-handle:hover{background:color-mix(in srgb,var(--mb-primary, #2563eb) 40%,transparent)}.mb-grid__column-menu{inset-block-start:calc(100% - 4px);inset-inline-end:6px;text-align:start}.mb-grid__menu-action{display:flex;align-items:center;width:100%;justify-content:flex-start;margin-block:4px}.mb-grid__menu-filter{display:grid;gap:8px;margin-block:8px;padding-block:8px;border-block:1px solid var(--mb-border, #e2e8f0)}.mb-grid__filter-control{display:grid;grid-template-columns:minmax(105px,.75fr) minmax(120px,1fr);gap:6px}.mb-grid__row{background:var(--mb-surface, #fff);transition:background .12s ease}.mb-grid__row:hover{background:var(--mb-hover, #f8fafc)}.mb-grid__row--selected{background:color-mix(in srgb,var(--mb-primary, #2563eb) 10%,#fff)}.mb-grid__cell--start{text-align:start}.mb-grid__cell--center{text-align:center}.mb-grid__cell--end{text-align:end}.mb-grid__select-cell{width:48px;min-width:48px;max-width:48px;text-align:center}.mb-grid__group-row{background:color-mix(in srgb,var(--mb-primary, #2563eb) 8%,#fff);cursor:pointer}.mb-grid__group-cell{padding:11px 14px;border-block-end:1px solid var(--mb-border, #e2e8f0);font-weight:600}.mb-grid__group-toggle{display:inline-flex;width:20px;justify-content:center;color:var(--mb-primary, #2563eb)}.mb-grid__detail-row td{padding:0;border-block-end:1px solid var(--mb-border, #e2e8f0);background:var(--mb-subtle, #f8fafc)}.mb-grid__empty{text-align:center;padding:36px 16px;color:var(--mb-muted, #64748b)}.mb-grid__footer-cell{font-weight:700;background:var(--mb-grid-footer-bg, #f8fafc)}.mb-grid__pager{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border-block-start:1px solid var(--mb-border, #e2e8f0);background:var(--mb-surface, #fff)}.mb-grid__pager-actions{display:flex;align-items:center;gap:6px;flex-wrap:wrap}.mb-grid__pager-info,.mb-grid__page-current{color:var(--mb-muted, #64748b);font-size:13px}.mb-grid__loading{position:absolute;z-index:30;inset:0;display:flex;align-items:center;justify-content:center;gap:10px;background:#ffffffad;backdrop-filter:blur(2px)}.mb-grid__spinner{width:24px;height:24px;border-radius:50%;border:3px solid var(--mb-border, #e2e8f0);border-block-start-color:var(--mb-primary, #2563eb);animation:mb-grid-spin .8s linear infinite}.cdk-drag-preview{box-sizing:border-box;border-radius:var(--mb-radius-md, 10px);box-shadow:var(--mb-shadow-lg, 0 16px 40px rgba(15, 23, 42, .16));background:#fff;padding:10px 14px}.cdk-drag-placeholder{opacity:.25}@keyframes mb-grid-spin{to{transform:rotate(360deg)}}@media (max-width: 720px){.mb-grid__toolbar,.mb-grid__pager{align-items:stretch;flex-direction:column}.mb-grid__toolbar-end,.mb-grid__pager-actions{width:100%}.mb-grid__search{width:100%;min-width:0}}.mb-grid__tool-button--primary{background:var(--mb-primary, #2563eb);border-color:var(--mb-primary, #2563eb);color:#fff}.mb-grid__command-cell{width:1%;white-space:nowrap;text-align:center}.mb-grid__command{min-height:30px;margin-inline:2px;padding:0 10px;border:1px solid var(--mb-border, #e2e8f0);border-radius:var(--mb-radius-sm, 8px);background:var(--mb-surface, #fff);color:var(--mb-text, #0f172a);cursor:pointer;font:inherit}.mb-grid__command:hover{border-color:var(--mb-primary, #2563eb);color:var(--mb-primary, #2563eb)}.mb-grid__command--danger:hover{border-color:var(--mb-danger, #dc2626);color:var(--mb-danger, #dc2626)}.mb-grid__row--editing{background:color-mix(in srgb,var(--mb-primary, #2563eb) 7%,var(--mb-surface, #fff))}.mb-grid__row--new{border-block-start:2px solid var(--mb-primary, #2563eb)}.mb-grid__editor{width:100%;min-height:34px;border:1px solid var(--mb-border, #e2e8f0);border-radius:var(--mb-radius-sm, 8px);background:var(--mb-input-bg, #fff);color:var(--mb-text, #0f172a);padding-inline:8px;font:inherit;outline:none}.mb-grid__editor:focus{border-color:var(--mb-primary, #2563eb);box-shadow:0 0 0 3px color-mix(in srgb,var(--mb-primary, #2563eb) 15%,transparent)}.mb-grid__error{display:block;margin-block-start:4px;color:var(--mb-danger, #dc2626);font-size:11px}.mb-grid__expand-button{border:0;background:transparent;cursor:pointer;font-size:16px;color:var(--mb-muted, #64748b)}.mb-grid__detail-row td{padding:14px;background:color-mix(in srgb,var(--mb-surface, #fff) 92%,var(--mb-primary, #2563eb) 8%)}\n"] }]
2028
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: MbGridStateService }, { type: undefined, decorators: [{
2029
+ type: Inject,
2030
+ args: [MB_UI_CONFIG]
2031
+ }] }, { type: undefined, decorators: [{
2032
+ type: Inject,
2033
+ args: [MB_GRID_MESSAGES]
2034
+ }] }], propDecorators: { data: [{
2035
+ type: Input
2036
+ }], columns: [{
2037
+ type: Input
2038
+ }], loading: [{
2039
+ type: Input
2040
+ }], dataMode: [{
2041
+ type: Input
2042
+ }], state: [{
2043
+ type: Input
2044
+ }], total: [{
2045
+ type: Input
2046
+ }], height: [{
2047
+ type: Input
2048
+ }], rowHeight: [{
2049
+ type: Input
2050
+ }], density: [{
2051
+ type: Input
2052
+ }], locale: [{
2053
+ type: Input
2054
+ }], dir: [{
2055
+ type: Input
2056
+ }], noRecordsText: [{
2057
+ type: Input
2058
+ }], loadingText: [{
2059
+ type: Input
2060
+ }], pageable: [{
2061
+ type: Input
2062
+ }], pageSizeOptions: [{
2063
+ type: Input
2064
+ }], sortable: [{
2065
+ type: Input
2066
+ }], multiSort: [{
2067
+ type: Input
2068
+ }], filterable: [{
2069
+ type: Input
2070
+ }], filterMode: [{
2071
+ type: Input
2072
+ }], groupable: [{
2073
+ type: Input
2074
+ }], selectable: [{
2075
+ type: Input
2076
+ }], selectionMode: [{
2077
+ type: Input
2078
+ }], rowKey: [{
2079
+ type: Input
2080
+ }], selectedKeys: [{
2081
+ type: Input
2082
+ }], resizable: [{
2083
+ type: Input
2084
+ }], reorderable: [{
2085
+ type: Input
2086
+ }], columnMenu: [{
2087
+ type: Input
2088
+ }], showToolbar: [{
2089
+ type: Input
2090
+ }], showSearch: [{
2091
+ type: Input
2092
+ }], showColumnChooser: [{
2093
+ type: Input
2094
+ }], showExport: [{
2095
+ type: Input
2096
+ }], showFooter: [{
2097
+ type: Input
2098
+ }], virtualScroll: [{
2099
+ type: Input
2100
+ }], exportFileName: [{
2101
+ type: Input
2102
+ }], excelFileName: [{
2103
+ type: Input
2104
+ }], jsonFileName: [{
2105
+ type: Input
2106
+ }], showExcelExport: [{
2107
+ type: Input
2108
+ }], showJsonExport: [{
2109
+ type: Input
2110
+ }], persistKey: [{
2111
+ type: Input
2112
+ }], autoPersist: [{
2113
+ type: Input
2114
+ }], editable: [{
2115
+ type: Input
2116
+ }], editMode: [{
2117
+ type: Input
2118
+ }], allowAdd: [{
2119
+ type: Input
2120
+ }], allowDelete: [{
2121
+ type: Input
2122
+ }], confirmDelete: [{
2123
+ type: Input
2124
+ }], commandColumnTitle: [{
2125
+ type: Input
2126
+ }], newItemFactory: [{
2127
+ type: Input
2128
+ }], expandable: [{
2129
+ type: Input
2130
+ }], detailInitiallyExpanded: [{
2131
+ type: Input
2132
+ }], rowClassName: [{
2133
+ type: Input
2134
+ }], responsiveColumns: [{
2135
+ type: Input
2136
+ }], stateChange: [{
2137
+ type: Output
2138
+ }], pageChange: [{
2139
+ type: Output
2140
+ }], sortChange: [{
2141
+ type: Output
2142
+ }], filterChange: [{
2143
+ type: Output
2144
+ }], groupChange: [{
2145
+ type: Output
2146
+ }], selectedKeysChange: [{
2147
+ type: Output
2148
+ }], columnResize: [{
2149
+ type: Output
2150
+ }], columnReorder: [{
2151
+ type: Output
2152
+ }], columnVisibilityChange: [{
2153
+ type: Output
2154
+ }], columnLockChange: [{
2155
+ type: Output
2156
+ }], selectionChange: [{
2157
+ type: Output
2158
+ }], edit: [{
2159
+ type: Output
2160
+ }], save: [{
2161
+ type: Output
2162
+ }], cancel: [{
2163
+ type: Output
2164
+ }], remove: [{
2165
+ type: Output
2166
+ }], detailToggle: [{
2167
+ type: Output
2168
+ }], rowClick: [{
2169
+ type: Output
2170
+ }], cellClick: [{
2171
+ type: Output
2172
+ }], cellTemplates: [{
2173
+ type: ContentChildren,
2174
+ args: [MbGridCellTemplateDirective]
2175
+ }], headerTemplates: [{
2176
+ type: ContentChildren,
2177
+ args: [MbGridHeaderTemplateDirective]
2178
+ }], editTemplates: [{
2179
+ type: ContentChildren,
2180
+ args: [MbGridEditTemplateDirective]
2181
+ }], footerTemplates: [{
2182
+ type: ContentChildren,
2183
+ args: [MbGridFooterTemplateDirective]
2184
+ }], toolbarTemplate: [{
2185
+ type: ContentChild,
2186
+ args: [MbGridToolbarTemplateDirective]
2187
+ }], detailTemplate: [{
2188
+ type: ContentChild,
2189
+ args: [MbGridDetailTemplateDirective]
2190
+ }] } });
2191
+
2192
+ function mbGridStateFromLegacy(state, defaultTake = 10) {
2193
+ if (!state)
2194
+ return mbGridDefaultState(defaultTake);
2195
+ return {
2196
+ skip: state.skip ?? 0,
2197
+ take: state.take ?? defaultTake,
2198
+ sort: (state.sort ?? []).filter((item) => item.dir).map((item) => ({
2199
+ field: item.field,
2200
+ dir: item.dir,
2201
+ })),
2202
+ filter: state.filter,
2203
+ group: (state.group ?? []).map((item) => ({
2204
+ field: item.field,
2205
+ dir: item.dir,
2206
+ })),
2207
+ };
2208
+ }
2209
+ function mbGridStateToLegacy(state) {
2210
+ return {
2211
+ skip: state.skip,
2212
+ take: state.take,
2213
+ sort: state.sort,
2214
+ filter: state.filter,
2215
+ group: state.group,
2216
+ };
2217
+ }
2218
+ function normalizeSortField(field) {
2219
+ const parts = field.split('.');
2220
+ if (parts.length > 1) {
2221
+ return `${parts[parts.length - 2]}.${parts[parts.length - 1]}`;
2222
+ }
2223
+ return field;
2224
+ }
2225
+ function appendFilterQuery(query, filter, startIndex = 1) {
2226
+ if (!filter?.filters?.length)
2227
+ return startIndex;
2228
+ let count = startIndex;
2229
+ for (const item of filter.filters) {
2230
+ if ('logic' in item && item.filters) {
2231
+ let addLogic = false;
2232
+ for (const innerItem of item.filters) {
2233
+ const descriptor = innerItem;
2234
+ let chunk = `filter[filters][${count}][field]=${encodeURIComponent(String(descriptor.field))}` +
2235
+ `&filter[filters][${count}][value]=${encodeURIComponent(String(descriptor.value ?? ''))}` +
2236
+ `&filter[filters][${count}][operator]=${encodeURIComponent(String(descriptor.operator))}`;
2237
+ if (item.logic && item.filters.length > 1 && !addLogic) {
2238
+ chunk += `&filter[filters][${count}][logic]=${encodeURIComponent(item.logic)}`;
2239
+ addLogic = true;
2240
+ }
2241
+ query.push(chunk);
2242
+ count += 1;
2243
+ }
2244
+ continue;
2245
+ }
2246
+ const descriptor = item;
2247
+ query.push(`filter[filters][${count}][field]=${encodeURIComponent(String(descriptor.field))}` +
2248
+ `&filter[filters][${count}][value]=${encodeURIComponent(String(descriptor.value ?? ''))}` +
2249
+ `&filter[filters][${count}][operator]=${encodeURIComponent(String(descriptor.operator))}`);
2250
+ count += 1;
2251
+ }
2252
+ return count;
2253
+ }
2254
+ /**
2255
+ * Builds a nested query string for APIs that expect indexed sort, filter, and group parameters.
2256
+ */
2257
+ function mbGridStateToNestedQuery(state) {
2258
+ const parts = [];
2259
+ if (state.skip) {
2260
+ parts.push(`skip=${state.skip}`);
2261
+ }
2262
+ if (state.take) {
2263
+ parts.push(`take=${state.take}`);
2264
+ }
2265
+ if (state.sort?.length) {
2266
+ let count = 1;
2267
+ for (const item of state.sort) {
2268
+ const field = normalizeSortField(String(item.field));
2269
+ parts.push(`sort[${count}][field]=${encodeURIComponent(field)}`);
2270
+ parts.push(`sort[${count}][dir]=${encodeURIComponent(item.dir)}`);
2271
+ count += 1;
2272
+ }
2273
+ }
2274
+ appendFilterQuery(parts, state.filter);
2275
+ if (state.group?.length) {
2276
+ let count = 1;
2277
+ for (const item of state.group) {
2278
+ parts.push(`group[${count}][field]=${encodeURIComponent(String(item.field))}`);
2279
+ if (item.dir) {
2280
+ parts.push(`group[${count}][dir]=${encodeURIComponent(item.dir)}`);
2281
+ }
2282
+ count += 1;
2283
+ }
2284
+ }
2285
+ return parts.join('&');
2286
+ }
2287
+
2288
+ const MB_DIALOG_DATA = new InjectionToken('MB_DIALOG_DATA');
2289
+
2290
+ class MbDialogRef {
2291
+ overlayRef;
2292
+ closedSubject = new Subject();
2293
+ afterClosed$ = this.closedSubject.asObservable();
2294
+ constructor(overlayRef) {
2295
+ this.overlayRef = overlayRef;
2296
+ }
2297
+ close(result) {
2298
+ if (!this.overlayRef.hasAttached())
2299
+ return;
2300
+ this.overlayRef.dispose();
2301
+ this.closedSubject.next(result);
2302
+ this.closedSubject.complete();
2303
+ }
2304
+ }
2305
+
2306
+ class MbDialogContainerComponent {
2307
+ ariaLabel = 'Dialog';
2308
+ width = '560px';
2309
+ maxWidth = 'calc(100vw - 2rem)';
2310
+ minWidth = '280px';
2311
+ portalOutlet;
2312
+ hostClass = 'mb-dialog-host';
2313
+ attachComponentPortal(portal) {
2314
+ return this.portalOutlet.attachComponentPortal(portal);
2315
+ }
2316
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbDialogContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2317
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbDialogContainerComponent, isStandalone: true, selector: "mb-dialog-container", inputs: { ariaLabel: "ariaLabel", width: "width", maxWidth: "maxWidth", minWidth: "minWidth" }, host: { properties: { "class": "this.hostClass" } }, viewQueries: [{ propertyName: "portalOutlet", first: true, predicate: CdkPortalOutlet, descendants: true, static: true }], ngImport: i0, template: "<section\n class=\"mb-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"ariaLabel\"\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"true\"\n [style.width]=\"width\"\n [style.max-width]=\"maxWidth\"\n [style.min-width]=\"minWidth\">\n <ng-template cdkPortalOutlet></ng-template>\n</section>\n", styles: [":host{display:block}.mb-dialog{background:var(--mb-color-surface-elevated);color:var(--mb-color-text);border:1px solid var(--mb-color-border);border-radius:var(--mb-radius-xl);box-shadow:var(--mb-shadow-lg);overflow:hidden;outline:none}\n"], dependencies: [{ kind: "ngmodule", type: PortalModule }, { kind: "directive", type: i1$1.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2318
+ }
2319
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbDialogContainerComponent, decorators: [{
2320
+ type: Component,
2321
+ args: [{ selector: 'mb-dialog-container', standalone: true, imports: [PortalModule, CdkTrapFocus], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section\n class=\"mb-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"ariaLabel\"\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"true\"\n [style.width]=\"width\"\n [style.max-width]=\"maxWidth\"\n [style.min-width]=\"minWidth\">\n <ng-template cdkPortalOutlet></ng-template>\n</section>\n", styles: [":host{display:block}.mb-dialog{background:var(--mb-color-surface-elevated);color:var(--mb-color-text);border:1px solid var(--mb-color-border);border-radius:var(--mb-radius-xl);box-shadow:var(--mb-shadow-lg);overflow:hidden;outline:none}\n"] }]
2322
+ }], propDecorators: { ariaLabel: [{
2323
+ type: Input
2324
+ }], width: [{
2325
+ type: Input
2326
+ }], maxWidth: [{
2327
+ type: Input
2328
+ }], minWidth: [{
2329
+ type: Input
2330
+ }], portalOutlet: [{
2331
+ type: ViewChild,
2332
+ args: [CdkPortalOutlet, { static: true }]
2333
+ }], hostClass: [{
2334
+ type: HostBinding,
2335
+ args: ['class']
2336
+ }] } });
2337
+
2338
+ class MbDialogService {
2339
+ overlay;
2340
+ injector;
2341
+ constructor(overlay, injector) {
2342
+ this.overlay = overlay;
2343
+ this.injector = injector;
2344
+ }
2345
+ open(component, config = {}) {
2346
+ const overlayRef = this.overlay.create(this.createOverlayConfig(config));
2347
+ const dialogRef = new MbDialogRef(overlayRef);
2348
+ const containerPortal = new ComponentPortal(MbDialogContainerComponent, null, this.injector);
2349
+ const containerRef = overlayRef.attach(containerPortal);
2350
+ containerRef.instance.ariaLabel = config.ariaLabel ?? 'Dialog';
2351
+ containerRef.instance.width = config.width ?? '560px';
2352
+ containerRef.instance.maxWidth = config.maxWidth ?? 'calc(100vw - 2rem)';
2353
+ containerRef.instance.minWidth = config.minWidth ?? '280px';
2354
+ const contentInjector = Injector.create({
2355
+ parent: this.injector,
2356
+ providers: [
2357
+ { provide: MbDialogRef, useValue: dialogRef },
2358
+ { provide: MB_DIALOG_DATA, useValue: config.data },
2359
+ ],
2360
+ });
2361
+ containerRef.instance.attachComponentPortal(new ComponentPortal(component, null, contentInjector));
2362
+ if (config.closeOnBackdropClick !== false) {
2363
+ overlayRef.backdropClick().pipe(take(1)).subscribe(() => dialogRef.close());
2364
+ }
2365
+ if (config.closeOnEscape !== false) {
2366
+ overlayRef
2367
+ .keydownEvents()
2368
+ .pipe(filter((event) => event.key === 'Escape'), take(1))
2369
+ .subscribe(() => dialogRef.close());
2370
+ }
2371
+ return dialogRef;
2372
+ }
2373
+ createOverlayConfig(config) {
2374
+ return new OverlayConfig({
2375
+ hasBackdrop: true,
2376
+ backdropClass: config.backdropClass ?? 'mb-overlay-backdrop',
2377
+ panelClass: ['mb-dialog-panel', ...(Array.isArray(config.panelClass) ? config.panelClass : config.panelClass ? [config.panelClass] : [])],
2378
+ scrollStrategy: this.overlay.scrollStrategies.block(),
2379
+ positionStrategy: this.overlay.position().global().centerHorizontally().centerVertically(),
2380
+ });
2381
+ }
2382
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbDialogService, deps: [{ token: i1$2.Overlay }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
2383
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbDialogService, providedIn: 'root' });
2384
+ }
2385
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbDialogService, decorators: [{
2386
+ type: Injectable,
2387
+ args: [{ providedIn: 'root' }]
2388
+ }], ctorParameters: () => [{ type: i1$2.Overlay }, { type: i0.Injector }] });
2389
+
2390
+ class MbDialogComponent {
2391
+ messages = inject(MB_UI_MESSAGES);
2392
+ open = false;
2393
+ title = '';
2394
+ width = '560px';
2395
+ height;
2396
+ minWidth = '280px';
2397
+ maxWidth = 'calc(100vw - 2rem)';
2398
+ closable = true;
2399
+ closeOnBackdrop = true;
2400
+ closeOnEscape = true;
2401
+ ariaLabel;
2402
+ get effectiveAriaLabel() {
2403
+ return this.ariaLabel ?? this.messages.dialog.ariaLabel;
2404
+ }
2405
+ get closeAriaLabel() {
2406
+ return this.messages.dialog.close;
2407
+ }
2408
+ close = new EventEmitter();
2409
+ onEscape() {
2410
+ if (this.open && this.closeOnEscape) {
2411
+ this.requestClose();
2412
+ }
2413
+ }
2414
+ get panelWidth() {
2415
+ return typeof this.width === 'number' ? `${this.width}px` : this.width;
2416
+ }
2417
+ get panelHeight() {
2418
+ if (this.height === undefined || this.height === null)
2419
+ return null;
2420
+ return typeof this.height === 'number' ? `${this.height}px` : this.height;
2421
+ }
2422
+ get panelMinWidth() {
2423
+ return typeof this.minWidth === 'number' ? `${this.minWidth}px` : this.minWidth;
2424
+ }
2425
+ get panelMaxWidth() {
2426
+ return typeof this.maxWidth === 'number' ? `${this.maxWidth}px` : this.maxWidth;
2427
+ }
2428
+ requestClose() {
2429
+ if (!this.closable)
2430
+ return;
2431
+ this.close.emit();
2432
+ }
2433
+ onBackdropClick() {
2434
+ if (this.closeOnBackdrop) {
2435
+ this.requestClose();
2436
+ }
2437
+ }
2438
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2439
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbDialogComponent, isStandalone: true, selector: "mb-dialog", inputs: { open: "open", title: "title", width: "width", height: "height", minWidth: "minWidth", maxWidth: "maxWidth", closable: "closable", closeOnBackdrop: "closeOnBackdrop", closeOnEscape: "closeOnEscape", ariaLabel: "ariaLabel" }, outputs: { close: "close" }, host: { listeners: { "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: "<div *ngIf=\"open\" class=\"mb-dialog-backdrop\" (click)=\"onBackdropClick()\">\r\n <section\r\n class=\"mb-dialog-panel\"\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n [attr.aria-label]=\"effectiveAriaLabel\"\r\n cdkTrapFocus\r\n [cdkTrapFocusAutoCapture]=\"true\"\r\n [style.width]=\"panelWidth\"\r\n [style.height]=\"panelHeight\"\r\n [style.min-width]=\"panelMinWidth\"\r\n [style.max-width]=\"panelMaxWidth\"\r\n (click)=\"$event.stopPropagation()\">\r\n <header *ngIf=\"title || closable\" class=\"mb-dialog-panel__titlebar\">\r\n <ng-content select=\"[mbDialogTitlebar],mb-dialog-titlebar\"></ng-content>\r\n <h2 *ngIf=\"title\" class=\"mb-dialog-panel__title\">{{ title }}</h2>\r\n <button\r\n *ngIf=\"closable\"\r\n type=\"button\"\r\n class=\"mb-dialog-panel__close\"\r\n [attr.aria-label]=\"closeAriaLabel\"\r\n (click)=\"requestClose()\">\u00D7</button>\r\n </header>\r\n\r\n <div class=\"mb-dialog-panel__content\">\r\n <ng-content></ng-content>\r\n </div>\r\n\r\n <footer class=\"mb-dialog-panel__actions\">\r\n <ng-content select=\"[mbDialogActions],mb-dialog-actions\"></ng-content>\r\n </footer>\r\n </section>\r\n</div>\r\n", styles: [".mb-dialog-backdrop{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;padding:1rem;background:#0f172a73}.mb-dialog-panel{display:flex;flex-direction:column;max-height:calc(100vh - 2rem);border-radius:var(--mb-radius-lg, 14px);background:var(--mb-surface, #fff);color:var(--mb-text, #0f172a);box-shadow:var(--mb-shadow-lg, 0 20px 40px rgba(15, 23, 42, .18));overflow:hidden}.mb-dialog-panel__titlebar{display:flex;align-items:center;gap:12px;padding:14px 16px;border-block-end:1px solid var(--mb-border, #e2e8f0);background:color-mix(in srgb,var(--mb-surface, #fff) 90%,var(--mb-primary, #2563eb) 10%)}.mb-dialog-panel__title{margin:0;flex:1;font-size:1rem;font-weight:700}.mb-dialog-panel__close{border:none;background:transparent;color:var(--mb-muted, #64748b);font-size:24px;line-height:1;cursor:pointer}.mb-dialog-panel__content{flex:1;overflow:auto;padding:16px}.mb-dialog-panel__actions{display:flex;justify-content:flex-end;gap:8px;padding:12px 16px;border-block-start:1px solid var(--mb-border, #e2e8f0)}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i1$3.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2440
+ }
2441
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbDialogComponent, decorators: [{
2442
+ type: Component,
2443
+ args: [{ selector: 'mb-dialog', standalone: true, imports: [NgIf, A11yModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div *ngIf=\"open\" class=\"mb-dialog-backdrop\" (click)=\"onBackdropClick()\">\r\n <section\r\n class=\"mb-dialog-panel\"\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n [attr.aria-label]=\"effectiveAriaLabel\"\r\n cdkTrapFocus\r\n [cdkTrapFocusAutoCapture]=\"true\"\r\n [style.width]=\"panelWidth\"\r\n [style.height]=\"panelHeight\"\r\n [style.min-width]=\"panelMinWidth\"\r\n [style.max-width]=\"panelMaxWidth\"\r\n (click)=\"$event.stopPropagation()\">\r\n <header *ngIf=\"title || closable\" class=\"mb-dialog-panel__titlebar\">\r\n <ng-content select=\"[mbDialogTitlebar],mb-dialog-titlebar\"></ng-content>\r\n <h2 *ngIf=\"title\" class=\"mb-dialog-panel__title\">{{ title }}</h2>\r\n <button\r\n *ngIf=\"closable\"\r\n type=\"button\"\r\n class=\"mb-dialog-panel__close\"\r\n [attr.aria-label]=\"closeAriaLabel\"\r\n (click)=\"requestClose()\">\u00D7</button>\r\n </header>\r\n\r\n <div class=\"mb-dialog-panel__content\">\r\n <ng-content></ng-content>\r\n </div>\r\n\r\n <footer class=\"mb-dialog-panel__actions\">\r\n <ng-content select=\"[mbDialogActions],mb-dialog-actions\"></ng-content>\r\n </footer>\r\n </section>\r\n</div>\r\n", styles: [".mb-dialog-backdrop{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;padding:1rem;background:#0f172a73}.mb-dialog-panel{display:flex;flex-direction:column;max-height:calc(100vh - 2rem);border-radius:var(--mb-radius-lg, 14px);background:var(--mb-surface, #fff);color:var(--mb-text, #0f172a);box-shadow:var(--mb-shadow-lg, 0 20px 40px rgba(15, 23, 42, .18));overflow:hidden}.mb-dialog-panel__titlebar{display:flex;align-items:center;gap:12px;padding:14px 16px;border-block-end:1px solid var(--mb-border, #e2e8f0);background:color-mix(in srgb,var(--mb-surface, #fff) 90%,var(--mb-primary, #2563eb) 10%)}.mb-dialog-panel__title{margin:0;flex:1;font-size:1rem;font-weight:700}.mb-dialog-panel__close{border:none;background:transparent;color:var(--mb-muted, #64748b);font-size:24px;line-height:1;cursor:pointer}.mb-dialog-panel__content{flex:1;overflow:auto;padding:16px}.mb-dialog-panel__actions{display:flex;justify-content:flex-end;gap:8px;padding:12px 16px;border-block-start:1px solid var(--mb-border, #e2e8f0)}\n"] }]
2444
+ }], propDecorators: { open: [{
2445
+ type: Input
2446
+ }], title: [{
2447
+ type: Input
2448
+ }], width: [{
2449
+ type: Input
2450
+ }], height: [{
2451
+ type: Input
2452
+ }], minWidth: [{
2453
+ type: Input
2454
+ }], maxWidth: [{
2455
+ type: Input
2456
+ }], closable: [{
2457
+ type: Input
2458
+ }], closeOnBackdrop: [{
2459
+ type: Input
2460
+ }], closeOnEscape: [{
2461
+ type: Input
2462
+ }], ariaLabel: [{
2463
+ type: Input
2464
+ }], close: [{
2465
+ type: Output
2466
+ }], onEscape: [{
2467
+ type: HostListener,
2468
+ args: ['document:keydown.escape']
2469
+ }] } });
2470
+
2471
+ /** Lightweight Jalali (Persian) calendar conversion utilities. */
2472
+ const MB_JALALI_MONTH_NAMES = [
2473
+ 'فروردین',
2474
+ 'اردیبهشت',
2475
+ 'خرداد',
2476
+ 'تیر',
2477
+ 'مرداد',
2478
+ 'شهریور',
2479
+ 'مهر',
2480
+ 'آبان',
2481
+ 'آذر',
2482
+ 'دی',
2483
+ 'بهمن',
2484
+ 'اسفند',
2485
+ ];
2486
+ function div(a, b) {
2487
+ return ~~(a / b);
2488
+ }
2489
+ function mod(a, b) {
2490
+ return a - ~~(a / b) * b;
2491
+ }
2492
+ function jalCal(jy) {
2493
+ const breaks = [
2494
+ -61, 9, 38, 199, 426, 686, 756, 818, 1111, 1181, 1210, 1635, 2060, 2097, 2192, 2262, 2324, 2394, 2456, 3178,
2495
+ ];
2496
+ let bl = breaks.length;
2497
+ let gy = jy + 621;
2498
+ let leapJ = -14;
2499
+ let jp = breaks[0];
2500
+ let jm = 0;
2501
+ let jump = 0;
2502
+ if (jy < jp || jy >= breaks[bl - 1]) {
2503
+ throw new Error(`Invalid Jalali year ${jy}`);
2504
+ }
2505
+ for (let i = 1; i < bl; i += 1) {
2506
+ jm = breaks[i];
2507
+ jump = jm - jp;
2508
+ if (jy < jm)
2509
+ break;
2510
+ leapJ = leapJ + div(jump, 33) * 8 + div(mod(jump, 33) + 3, 4);
2511
+ jp = jm;
2512
+ }
2513
+ const n = jy - jp;
2514
+ leapJ = leapJ + div(n, 33) * 8 + div(mod(n, 33) + 3, 4);
2515
+ if (mod(jump, 33) === 4 && jump - n === 4)
2516
+ leapJ += 1;
2517
+ const leapG = div(gy, 4) - div((div(gy, 100) + 1) * 3, 4) - 150;
2518
+ const march = 20 + leapJ - leapG;
2519
+ return jy < 100 ? [jump, march, 0] : [jump, march, div(jump, 33) * 8 + div(mod(jump, 33) + 3, 4)];
2520
+ }
2521
+ function j2d(jy, jm, jd) {
2522
+ const [r, march] = jalCal(jy);
2523
+ return g2d(jy + 621, 3, march) + (jm - 1) * 31 - div(jm, 7) * (jm - 7) + jd - 1;
2524
+ }
2525
+ function d2j(jdn) {
2526
+ const gy = d2g(jdn).year;
2527
+ let jy = gy - 621;
2528
+ const [_, march, leap] = jalCal(jy);
2529
+ let jdn1f = g2d(gy, 3, march);
2530
+ let k = jdn - jdn1f;
2531
+ if (k >= 0) {
2532
+ if (k <= 185) {
2533
+ return { year: jy, month: 1 + div(k, 31), day: mod(k, 31) + 1 };
2534
+ }
2535
+ k -= 186;
2536
+ }
2537
+ else {
2538
+ jy -= 1;
2539
+ k += 179;
2540
+ if (leap === 1)
2541
+ k += 1;
2542
+ }
2543
+ return { year: jy, month: 7 + div(k, 30), day: mod(k, 30) + 1 };
2544
+ }
2545
+ function g2d(gy, gm, gd) {
2546
+ let d = div((gy + div(gm - 8, 6) + 100100) * 1461, 4) +
2547
+ div(153 * mod(gm + 9, 12) + 2, 5) +
2548
+ gd -
2549
+ 34840408;
2550
+ d = d - div(div(gy + 100100 + div(gm - 8, 6), 100) * 3, 4) + 752;
2551
+ return d;
2552
+ }
2553
+ function d2g(jdn) {
2554
+ let j = 4 * jdn + 139361631 + div(div(4 * jdn + 183187720, 146097) * 3, 4) * 4 - 3908;
2555
+ const i = div(mod(j, 1461), 4) * 5 + 308;
2556
+ const gd = div(mod(i, 153), 5) + 1;
2557
+ const gm = mod(div(i, 153), 12) + 1;
2558
+ const gy = div(j, 1461) - 100100 + div(8 - gm, 6);
2559
+ return { year: gy, month: gm, day: gd };
2560
+ }
2561
+ function mbGregorianToJalali(date) {
2562
+ return d2j(g2d(date.year, date.month, date.day));
2563
+ }
2564
+ function mbJalaliToGregorian(date) {
2565
+ const jdn = j2d(date.year, date.month, date.day);
2566
+ return d2g(jdn);
2567
+ }
2568
+ function mbParseIsoDate(value) {
2569
+ if (!value)
2570
+ return null;
2571
+ const parts = value.split('-').map(Number);
2572
+ if (parts.length < 3 || parts.some(Number.isNaN))
2573
+ return null;
2574
+ return { year: parts[0], month: parts[1], day: parts[2] };
2575
+ }
2576
+ function mbToIsoDate(date) {
2577
+ return `${date.year}-${String(date.month).padStart(2, '0')}-${String(date.day).padStart(2, '0')}`;
2578
+ }
2579
+ function mbJalaliMonthLength(year, month) {
2580
+ if (month <= 6)
2581
+ return 31;
2582
+ if (month <= 11)
2583
+ return 30;
2584
+ const [, , leap] = jalCal(year);
2585
+ return leap + 29;
2586
+ }
2587
+ function mbFormatJalali(date, separator = '/') {
2588
+ return `${date.year}${separator}${String(date.month).padStart(2, '0')}${separator}${String(date.day).padStart(2, '0')}`;
2589
+ }
2590
+
2591
+ class MbToastService {
2592
+ toastsSubject = new BehaviorSubject([]);
2593
+ toasts$ = this.toastsSubject.asObservable();
2594
+ show(message, options = {}) {
2595
+ const id = globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`;
2596
+ const toast = {
2597
+ id,
2598
+ message,
2599
+ title: options.title,
2600
+ variant: options.variant ?? 'info',
2601
+ duration: options.duration ?? 4000,
2602
+ };
2603
+ this.toastsSubject.next([...this.toastsSubject.value, toast]);
2604
+ if (toast.duration > 0) {
2605
+ globalThis.setTimeout(() => this.dismiss(id), toast.duration);
2606
+ }
2607
+ return id;
2608
+ }
2609
+ success(message, title) {
2610
+ return this.show(message, { title, variant: 'success' });
2611
+ }
2612
+ info(message, title) {
2613
+ return this.show(message, { title, variant: 'info' });
2614
+ }
2615
+ warning(message, title) {
2616
+ return this.show(message, { title, variant: 'warning' });
2617
+ }
2618
+ danger(message, title) {
2619
+ return this.show(message, { title, variant: 'danger' });
2620
+ }
2621
+ dismiss(id) {
2622
+ this.toastsSubject.next(this.toastsSubject.value.filter((toast) => toast.id !== id));
2623
+ }
2624
+ clear() {
2625
+ this.toastsSubject.next([]);
2626
+ }
2627
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2628
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbToastService, providedIn: 'root' });
2629
+ }
2630
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbToastService, decorators: [{
2631
+ type: Injectable,
2632
+ args: [{ providedIn: 'root' }]
2633
+ }] });
2634
+
2635
+ class MbToastContainerComponent {
2636
+ toastService;
2637
+ messages = inject(MB_UI_MESSAGES);
2638
+ toasts$;
2639
+ constructor(toastService) {
2640
+ this.toastService = toastService;
2641
+ this.toasts$ = this.toastService.toasts$;
2642
+ }
2643
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbToastContainerComponent, deps: [{ token: MbToastService }], target: i0.ɵɵFactoryTarget.Component });
2644
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbToastContainerComponent, isStandalone: true, selector: "mb-toast-container", ngImport: i0, template: "<div class=\"mb-toast-container\" aria-live=\"polite\" aria-atomic=\"true\">\n <article\n *ngFor=\"let toast of toasts$ | async\"\n class=\"mb-toast\"\n [ngClass]=\"'mb-toast--' + toast.variant\">\n <div class=\"mb-toast__content\">\n <strong *ngIf=\"toast.title\" class=\"mb-toast__title\">{{ toast.title }}</strong>\n <p class=\"mb-toast__message\">{{ toast.message }}</p>\n </div>\n <button type=\"button\" class=\"mb-toast__close\" [attr.aria-label]=\"messages.common.close\" (click)=\"toastService.dismiss(toast.id)\">\u00D7</button>\n </article>\n</div>\n", styles: [".mb-toast-container{position:fixed;inset-block-start:1rem;inset-inline-end:1rem;z-index:1100;display:flex;flex-direction:column;gap:.75rem;inline-size:min(24rem,100vw - 2rem);pointer-events:none}.mb-toast{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;border:1px solid var(--mb-color-border);border-inline-start:4px solid var(--mb-color-primary);background:var(--mb-color-surface-elevated);color:var(--mb-color-text);border-radius:var(--mb-radius-lg);box-shadow:var(--mb-shadow-md);padding:.875rem;pointer-events:auto}.mb-toast--success{border-inline-start-color:var(--mb-color-success)}.mb-toast--warning{border-inline-start-color:var(--mb-color-warning)}.mb-toast--danger{border-inline-start-color:var(--mb-color-danger)}.mb-toast__title{display:block;margin-block-end:.2rem;font-size:.875rem}.mb-toast__message{margin:0;color:var(--mb-color-muted);font-size:.8125rem}.mb-toast__close{border:0;background:transparent;color:var(--mb-color-muted);cursor:pointer;font-size:1.25rem;line-height:1}\n"], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2645
+ }
2646
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbToastContainerComponent, decorators: [{
2647
+ type: Component,
2648
+ args: [{ selector: 'mb-toast-container', standalone: true, imports: [AsyncPipe, NgFor, NgIf, NgClass], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mb-toast-container\" aria-live=\"polite\" aria-atomic=\"true\">\n <article\n *ngFor=\"let toast of toasts$ | async\"\n class=\"mb-toast\"\n [ngClass]=\"'mb-toast--' + toast.variant\">\n <div class=\"mb-toast__content\">\n <strong *ngIf=\"toast.title\" class=\"mb-toast__title\">{{ toast.title }}</strong>\n <p class=\"mb-toast__message\">{{ toast.message }}</p>\n </div>\n <button type=\"button\" class=\"mb-toast__close\" [attr.aria-label]=\"messages.common.close\" (click)=\"toastService.dismiss(toast.id)\">\u00D7</button>\n </article>\n</div>\n", styles: [".mb-toast-container{position:fixed;inset-block-start:1rem;inset-inline-end:1rem;z-index:1100;display:flex;flex-direction:column;gap:.75rem;inline-size:min(24rem,100vw - 2rem);pointer-events:none}.mb-toast{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;border:1px solid var(--mb-color-border);border-inline-start:4px solid var(--mb-color-primary);background:var(--mb-color-surface-elevated);color:var(--mb-color-text);border-radius:var(--mb-radius-lg);box-shadow:var(--mb-shadow-md);padding:.875rem;pointer-events:auto}.mb-toast--success{border-inline-start-color:var(--mb-color-success)}.mb-toast--warning{border-inline-start-color:var(--mb-color-warning)}.mb-toast--danger{border-inline-start-color:var(--mb-color-danger)}.mb-toast__title{display:block;margin-block-end:.2rem;font-size:.875rem}.mb-toast__message{margin:0;color:var(--mb-color-muted);font-size:.8125rem}.mb-toast__close{border:0;background:transparent;color:var(--mb-color-muted);cursor:pointer;font-size:1.25rem;line-height:1}\n"] }]
2649
+ }], ctorParameters: () => [{ type: MbToastService }] });
2650
+
2651
+ class MbCheckboxComponent {
2652
+ label = '';
2653
+ hint = '';
2654
+ size = 'md';
2655
+ indeterminate = false;
2656
+ disabled = false;
2657
+ name;
2658
+ checkedChange = new EventEmitter();
2659
+ hostClass = 'mb-checkbox-host';
2660
+ checked = false;
2661
+ onChange = () => undefined;
2662
+ onTouched = () => undefined;
2663
+ writeValue(value) {
2664
+ this.checked = !!value;
2665
+ }
2666
+ registerOnChange(fn) {
2667
+ this.onChange = fn;
2668
+ }
2669
+ registerOnTouched(fn) {
2670
+ this.onTouched = fn;
2671
+ }
2672
+ setDisabledState(isDisabled) {
2673
+ this.disabled = isDisabled;
2674
+ }
2675
+ blur() { this.onTouched(); }
2676
+ toggle() {
2677
+ if (this.disabled)
2678
+ return;
2679
+ this.checked = !this.checked;
2680
+ this.indeterminate = false;
2681
+ this.onChange(this.checked);
2682
+ this.checkedChange.emit(this.checked);
2683
+ this.onTouched();
2684
+ }
2685
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbCheckboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2686
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbCheckboxComponent, isStandalone: true, selector: "mb-checkbox", inputs: { label: "label", hint: "hint", size: "size", indeterminate: "indeterminate", disabled: "disabled", name: "name" }, outputs: { checkedChange: "checkedChange" }, host: { properties: { "class": "this.hostClass" } }, providers: [
2687
+ {
2688
+ provide: NG_VALUE_ACCESSOR,
2689
+ useExisting: forwardRef(() => MbCheckboxComponent),
2690
+ multi: true,
2691
+ },
2692
+ ], ngImport: i0, template: "<label class=\"mb-checkbox\" [class.mb-checkbox--disabled]=\"disabled\" [ngClass]=\"'mb-checkbox--' + size\">\n <input\n class=\"mb-checkbox__native\"\n type=\"checkbox\"\n [name]=\"name\"\n [checked]=\"checked\"\n [disabled]=\"disabled\"\n [attr.aria-checked]=\"indeterminate ? 'mixed' : checked\"\n (change)=\"toggle()\"\n (blur)=\"blur()\"\n />\n <span class=\"mb-checkbox__box\" [class.mb-checkbox__box--indeterminate]=\"indeterminate\">\n <span *ngIf=\"checked && !indeterminate\" class=\"mb-checkbox__mark\">\u2713</span>\n <span *ngIf=\"indeterminate\" class=\"mb-checkbox__dash\"></span>\n </span>\n <span class=\"mb-checkbox__content\" *ngIf=\"label || hint\">\n <span class=\"mb-checkbox__label\" *ngIf=\"label\">{{ label }}</span>\n <span class=\"mb-checkbox__hint\" *ngIf=\"hint\">{{ hint }}</span>\n </span>\n</label>\n", styles: [":host{display:inline-flex}.mb-checkbox{display:inline-flex;align-items:flex-start;gap:.55rem;cursor:pointer;color:var(--mb-color-text);-webkit-user-select:none;user-select:none}.mb-checkbox__native{position:absolute;opacity:0;pointer-events:none}.mb-checkbox__box{inline-size:1.1rem;block-size:1.1rem;border:1px solid var(--mb-color-border-strong);border-radius:var(--mb-radius-xs);background:var(--mb-color-bg);display:inline-grid;place-items:center;transition:var(--mb-transition-fast);margin-block-start:.08rem}.mb-checkbox:hover .mb-checkbox__box{border-color:var(--mb-color-primary)}.mb-checkbox__native:checked+.mb-checkbox__box,.mb-checkbox__box--indeterminate{background:var(--mb-color-primary);border-color:var(--mb-color-primary);color:#fff}.mb-checkbox__native:focus-visible+.mb-checkbox__box{box-shadow:var(--mb-focus-ring)}.mb-checkbox__mark{font-size:.78rem;line-height:1;font-weight:800}.mb-checkbox__dash{inline-size:.55rem;block-size:2px;background:currentColor;border-radius:999px}.mb-checkbox__content{display:grid;gap:.12rem}.mb-checkbox__label{font-weight:600;font-size:.9rem}.mb-checkbox__hint{color:var(--mb-color-muted);font-size:.78rem}.mb-checkbox--sm .mb-checkbox__box{inline-size:.95rem;block-size:.95rem}.mb-checkbox--lg .mb-checkbox__box{inline-size:1.25rem;block-size:1.25rem}.mb-checkbox--disabled{cursor:not-allowed;opacity:.62}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2693
+ }
2694
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbCheckboxComponent, decorators: [{
2695
+ type: Component,
2696
+ args: [{ selector: 'mb-checkbox', standalone: true, imports: [NgClass, NgIf], providers: [
2697
+ {
2698
+ provide: NG_VALUE_ACCESSOR,
2699
+ useExisting: forwardRef(() => MbCheckboxComponent),
2700
+ multi: true,
2701
+ },
2702
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<label class=\"mb-checkbox\" [class.mb-checkbox--disabled]=\"disabled\" [ngClass]=\"'mb-checkbox--' + size\">\n <input\n class=\"mb-checkbox__native\"\n type=\"checkbox\"\n [name]=\"name\"\n [checked]=\"checked\"\n [disabled]=\"disabled\"\n [attr.aria-checked]=\"indeterminate ? 'mixed' : checked\"\n (change)=\"toggle()\"\n (blur)=\"blur()\"\n />\n <span class=\"mb-checkbox__box\" [class.mb-checkbox__box--indeterminate]=\"indeterminate\">\n <span *ngIf=\"checked && !indeterminate\" class=\"mb-checkbox__mark\">\u2713</span>\n <span *ngIf=\"indeterminate\" class=\"mb-checkbox__dash\"></span>\n </span>\n <span class=\"mb-checkbox__content\" *ngIf=\"label || hint\">\n <span class=\"mb-checkbox__label\" *ngIf=\"label\">{{ label }}</span>\n <span class=\"mb-checkbox__hint\" *ngIf=\"hint\">{{ hint }}</span>\n </span>\n</label>\n", styles: [":host{display:inline-flex}.mb-checkbox{display:inline-flex;align-items:flex-start;gap:.55rem;cursor:pointer;color:var(--mb-color-text);-webkit-user-select:none;user-select:none}.mb-checkbox__native{position:absolute;opacity:0;pointer-events:none}.mb-checkbox__box{inline-size:1.1rem;block-size:1.1rem;border:1px solid var(--mb-color-border-strong);border-radius:var(--mb-radius-xs);background:var(--mb-color-bg);display:inline-grid;place-items:center;transition:var(--mb-transition-fast);margin-block-start:.08rem}.mb-checkbox:hover .mb-checkbox__box{border-color:var(--mb-color-primary)}.mb-checkbox__native:checked+.mb-checkbox__box,.mb-checkbox__box--indeterminate{background:var(--mb-color-primary);border-color:var(--mb-color-primary);color:#fff}.mb-checkbox__native:focus-visible+.mb-checkbox__box{box-shadow:var(--mb-focus-ring)}.mb-checkbox__mark{font-size:.78rem;line-height:1;font-weight:800}.mb-checkbox__dash{inline-size:.55rem;block-size:2px;background:currentColor;border-radius:999px}.mb-checkbox__content{display:grid;gap:.12rem}.mb-checkbox__label{font-weight:600;font-size:.9rem}.mb-checkbox__hint{color:var(--mb-color-muted);font-size:.78rem}.mb-checkbox--sm .mb-checkbox__box{inline-size:.95rem;block-size:.95rem}.mb-checkbox--lg .mb-checkbox__box{inline-size:1.25rem;block-size:1.25rem}.mb-checkbox--disabled{cursor:not-allowed;opacity:.62}\n"] }]
2703
+ }], propDecorators: { label: [{
2704
+ type: Input
2705
+ }], hint: [{
2706
+ type: Input
2707
+ }], size: [{
2708
+ type: Input
2709
+ }], indeterminate: [{
2710
+ type: Input
2711
+ }], disabled: [{
2712
+ type: Input
2713
+ }], name: [{
2714
+ type: Input
2715
+ }], checkedChange: [{
2716
+ type: Output
2717
+ }], hostClass: [{
2718
+ type: HostBinding,
2719
+ args: ['class']
2720
+ }] } });
2721
+
2722
+ class MbSwitchComponent {
2723
+ label = '';
2724
+ hint = '';
2725
+ size = 'md';
2726
+ disabled = false;
2727
+ checkedChange = new EventEmitter();
2728
+ hostClass = 'mb-switch-host';
2729
+ checked = false;
2730
+ onChange = () => undefined;
2731
+ onTouched = () => undefined;
2732
+ writeValue(value) { this.checked = !!value; }
2733
+ registerOnChange(fn) { this.onChange = fn; }
2734
+ registerOnTouched(fn) { this.onTouched = fn; }
2735
+ setDisabledState(isDisabled) { this.disabled = isDisabled; }
2736
+ toggle() {
2737
+ if (this.disabled)
2738
+ return;
2739
+ this.checked = !this.checked;
2740
+ this.onChange(this.checked);
2741
+ this.checkedChange.emit(this.checked);
2742
+ this.onTouched();
2743
+ }
2744
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbSwitchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2745
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbSwitchComponent, isStandalone: true, selector: "mb-switch", inputs: { label: "label", hint: "hint", size: "size", disabled: "disabled" }, outputs: { checkedChange: "checkedChange" }, host: { properties: { "class": "this.hostClass" } }, providers: [
2746
+ {
2747
+ provide: NG_VALUE_ACCESSOR,
2748
+ useExisting: forwardRef(() => MbSwitchComponent),
2749
+ multi: true,
2750
+ },
2751
+ ], ngImport: i0, template: "<button\n type=\"button\"\n class=\"mb-switch\"\n [class.mb-switch--checked]=\"checked\"\n [class.mb-switch--disabled]=\"disabled\"\n [ngClass]=\"'mb-switch--' + size\"\n [disabled]=\"disabled\"\n role=\"switch\"\n [attr.aria-checked]=\"checked\"\n (click)=\"toggle()\">\n <span class=\"mb-switch__track\"><span class=\"mb-switch__thumb\"></span></span>\n <span class=\"mb-switch__content\" *ngIf=\"label || hint\">\n <span class=\"mb-switch__label\" *ngIf=\"label\">{{ label }}</span>\n <span class=\"mb-switch__hint\" *ngIf=\"hint\">{{ hint }}</span>\n </span>\n</button>\n", styles: [":host{display:inline-flex}.mb-switch{border:0;background:transparent;color:var(--mb-color-text);display:inline-flex;align-items:center;gap:.6rem;cursor:pointer;padding:0;font:inherit;text-align:start}.mb-switch__track{inline-size:2.45rem;block-size:1.35rem;border-radius:999px;background:var(--mb-color-border-strong);padding:.15rem;transition:var(--mb-transition-fast);display:flex;align-items:center}.mb-switch__thumb{inline-size:1.05rem;block-size:1.05rem;border-radius:999px;background:#fff;box-shadow:var(--mb-shadow-sm);transition:transform var(--mb-transition-fast)}.mb-switch--checked .mb-switch__track{background:var(--mb-color-primary)}.mb-switch--checked .mb-switch__thumb{transform:translate(1.1rem)}:host-context([dir=rtl]) .mb-switch--checked .mb-switch__thumb,:host-context(.mb-rtl) .mb-switch--checked .mb-switch__thumb{transform:translate(-1.1rem)}.mb-switch:focus-visible .mb-switch__track{box-shadow:var(--mb-focus-ring)}.mb-switch__content{display:grid;gap:.1rem}.mb-switch__label{font-size:.9rem;font-weight:700}.mb-switch__hint{font-size:.78rem;color:var(--mb-color-muted)}.mb-switch--sm .mb-switch__track{inline-size:2rem;block-size:1.1rem}.mb-switch--sm .mb-switch__thumb{inline-size:.82rem;block-size:.82rem}.mb-switch--sm.mb-switch--checked .mb-switch__thumb{transform:translate(.9rem)}:host-context([dir=rtl]) .mb-switch--sm.mb-switch--checked .mb-switch__thumb,:host-context(.mb-rtl) .mb-switch--sm.mb-switch--checked .mb-switch__thumb{transform:translate(-.9rem)}.mb-switch--lg .mb-switch__track{inline-size:2.9rem;block-size:1.55rem}.mb-switch--lg .mb-switch__thumb{inline-size:1.25rem;block-size:1.25rem}.mb-switch--lg.mb-switch--checked .mb-switch__thumb{transform:translate(1.35rem)}:host-context([dir=rtl]) .mb-switch--lg.mb-switch--checked .mb-switch__thumb,:host-context(.mb-rtl) .mb-switch--lg.mb-switch--checked .mb-switch__thumb{transform:translate(-1.35rem)}.mb-switch--disabled{opacity:.62;cursor:not-allowed}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2752
+ }
2753
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbSwitchComponent, decorators: [{
2754
+ type: Component,
2755
+ args: [{ selector: 'mb-switch', standalone: true, imports: [NgClass, NgIf], providers: [
2756
+ {
2757
+ provide: NG_VALUE_ACCESSOR,
2758
+ useExisting: forwardRef(() => MbSwitchComponent),
2759
+ multi: true,
2760
+ },
2761
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n type=\"button\"\n class=\"mb-switch\"\n [class.mb-switch--checked]=\"checked\"\n [class.mb-switch--disabled]=\"disabled\"\n [ngClass]=\"'mb-switch--' + size\"\n [disabled]=\"disabled\"\n role=\"switch\"\n [attr.aria-checked]=\"checked\"\n (click)=\"toggle()\">\n <span class=\"mb-switch__track\"><span class=\"mb-switch__thumb\"></span></span>\n <span class=\"mb-switch__content\" *ngIf=\"label || hint\">\n <span class=\"mb-switch__label\" *ngIf=\"label\">{{ label }}</span>\n <span class=\"mb-switch__hint\" *ngIf=\"hint\">{{ hint }}</span>\n </span>\n</button>\n", styles: [":host{display:inline-flex}.mb-switch{border:0;background:transparent;color:var(--mb-color-text);display:inline-flex;align-items:center;gap:.6rem;cursor:pointer;padding:0;font:inherit;text-align:start}.mb-switch__track{inline-size:2.45rem;block-size:1.35rem;border-radius:999px;background:var(--mb-color-border-strong);padding:.15rem;transition:var(--mb-transition-fast);display:flex;align-items:center}.mb-switch__thumb{inline-size:1.05rem;block-size:1.05rem;border-radius:999px;background:#fff;box-shadow:var(--mb-shadow-sm);transition:transform var(--mb-transition-fast)}.mb-switch--checked .mb-switch__track{background:var(--mb-color-primary)}.mb-switch--checked .mb-switch__thumb{transform:translate(1.1rem)}:host-context([dir=rtl]) .mb-switch--checked .mb-switch__thumb,:host-context(.mb-rtl) .mb-switch--checked .mb-switch__thumb{transform:translate(-1.1rem)}.mb-switch:focus-visible .mb-switch__track{box-shadow:var(--mb-focus-ring)}.mb-switch__content{display:grid;gap:.1rem}.mb-switch__label{font-size:.9rem;font-weight:700}.mb-switch__hint{font-size:.78rem;color:var(--mb-color-muted)}.mb-switch--sm .mb-switch__track{inline-size:2rem;block-size:1.1rem}.mb-switch--sm .mb-switch__thumb{inline-size:.82rem;block-size:.82rem}.mb-switch--sm.mb-switch--checked .mb-switch__thumb{transform:translate(.9rem)}:host-context([dir=rtl]) .mb-switch--sm.mb-switch--checked .mb-switch__thumb,:host-context(.mb-rtl) .mb-switch--sm.mb-switch--checked .mb-switch__thumb{transform:translate(-.9rem)}.mb-switch--lg .mb-switch__track{inline-size:2.9rem;block-size:1.55rem}.mb-switch--lg .mb-switch__thumb{inline-size:1.25rem;block-size:1.25rem}.mb-switch--lg.mb-switch--checked .mb-switch__thumb{transform:translate(1.35rem)}:host-context([dir=rtl]) .mb-switch--lg.mb-switch--checked .mb-switch__thumb,:host-context(.mb-rtl) .mb-switch--lg.mb-switch--checked .mb-switch__thumb{transform:translate(-1.35rem)}.mb-switch--disabled{opacity:.62;cursor:not-allowed}\n"] }]
2762
+ }], propDecorators: { label: [{
2763
+ type: Input
2764
+ }], hint: [{
2765
+ type: Input
2766
+ }], size: [{
2767
+ type: Input
2768
+ }], disabled: [{
2769
+ type: Input
2770
+ }], checkedChange: [{
2771
+ type: Output
2772
+ }], hostClass: [{
2773
+ type: HostBinding,
2774
+ args: ['class']
2775
+ }] } });
2776
+
2777
+ class MbRadioGroupComponent {
2778
+ options = [];
2779
+ name = `mb-radio-${Math.random().toString(36).slice(2)}`;
2780
+ size = 'md';
2781
+ direction = 'vertical';
2782
+ disabled = false;
2783
+ valueChange = new EventEmitter();
2784
+ hostClass = 'mb-radio-group-host';
2785
+ value = null;
2786
+ onChange = () => undefined;
2787
+ onTouched = () => undefined;
2788
+ writeValue(value) { this.value = value; }
2789
+ registerOnChange(fn) { this.onChange = fn; }
2790
+ registerOnTouched(fn) { this.onTouched = fn; }
2791
+ setDisabledState(isDisabled) { this.disabled = isDisabled; }
2792
+ choose(option) {
2793
+ if (this.disabled || option.disabled)
2794
+ return;
2795
+ this.value = option.value;
2796
+ this.onChange(option.value);
2797
+ this.valueChange.emit(option.value);
2798
+ this.onTouched();
2799
+ }
2800
+ trackByValue(index) { return index; }
2801
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbRadioGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2802
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbRadioGroupComponent, isStandalone: true, selector: "mb-radio-group", inputs: { options: "options", name: "name", size: "size", direction: "direction", disabled: "disabled" }, outputs: { valueChange: "valueChange" }, host: { properties: { "class": "this.hostClass" } }, providers: [
2803
+ {
2804
+ provide: NG_VALUE_ACCESSOR,
2805
+ useExisting: forwardRef(() => MbRadioGroupComponent),
2806
+ multi: true,
2807
+ },
2808
+ ], ngImport: i0, template: "<div class=\"mb-radio-group\" [class.mb-radio-group--horizontal]=\"direction === 'horizontal'\" [ngClass]=\"'mb-radio-group--' + size\" role=\"radiogroup\">\n <label\n *ngFor=\"let option of options; trackBy: trackByValue\"\n class=\"mb-radio\"\n [class.mb-radio--checked]=\"value === option.value\"\n [class.mb-radio--disabled]=\"disabled || option.disabled\">\n <input\n class=\"mb-radio__native\"\n type=\"radio\"\n [name]=\"name\"\n [checked]=\"value === option.value\"\n [disabled]=\"disabled || option.disabled\"\n (change)=\"choose(option)\" />\n <span class=\"mb-radio__circle\"><span class=\"mb-radio__dot\"></span></span>\n <span class=\"mb-radio__content\">\n <span class=\"mb-radio__label\">{{ option.label }}</span>\n <span class=\"mb-radio__hint\" *ngIf=\"option.hint\">{{ option.hint }}</span>\n </span>\n </label>\n</div>\n", styles: [".mb-radio-group{display:grid;gap:.7rem}.mb-radio-group--horizontal{display:flex;flex-wrap:wrap;gap:.9rem 1.4rem}.mb-radio{display:inline-flex;align-items:flex-start;gap:.55rem;cursor:pointer;-webkit-user-select:none;user-select:none}.mb-radio__native{position:absolute;opacity:0;pointer-events:none}.mb-radio__circle{inline-size:1.1rem;block-size:1.1rem;border:1px solid var(--mb-color-border-strong);border-radius:999px;background:var(--mb-color-bg);display:grid;place-items:center;margin-block-start:.08rem;transition:var(--mb-transition-fast)}.mb-radio__dot{inline-size:.52rem;block-size:.52rem;border-radius:999px;background:transparent;transform:scale(.5);transition:var(--mb-transition-fast)}.mb-radio:hover .mb-radio__circle,.mb-radio--checked .mb-radio__circle{border-color:var(--mb-color-primary)}.mb-radio--checked .mb-radio__dot{background:var(--mb-color-primary);transform:scale(1)}.mb-radio__native:focus-visible+.mb-radio__circle{box-shadow:var(--mb-focus-ring)}.mb-radio__content{display:grid;gap:.1rem}.mb-radio__label{font-weight:650;font-size:.9rem;color:var(--mb-color-text)}.mb-radio__hint{color:var(--mb-color-muted);font-size:.78rem}.mb-radio--disabled{opacity:.62;cursor:not-allowed}.mb-radio-group--sm .mb-radio__circle{inline-size:.95rem;block-size:.95rem}.mb-radio-group--lg .mb-radio__circle{inline-size:1.25rem;block-size:1.25rem}\n"], dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2809
+ }
2810
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbRadioGroupComponent, decorators: [{
2811
+ type: Component,
2812
+ args: [{ selector: 'mb-radio-group', standalone: true, imports: [NgFor, NgIf, NgClass], providers: [
2813
+ {
2814
+ provide: NG_VALUE_ACCESSOR,
2815
+ useExisting: forwardRef(() => MbRadioGroupComponent),
2816
+ multi: true,
2817
+ },
2818
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mb-radio-group\" [class.mb-radio-group--horizontal]=\"direction === 'horizontal'\" [ngClass]=\"'mb-radio-group--' + size\" role=\"radiogroup\">\n <label\n *ngFor=\"let option of options; trackBy: trackByValue\"\n class=\"mb-radio\"\n [class.mb-radio--checked]=\"value === option.value\"\n [class.mb-radio--disabled]=\"disabled || option.disabled\">\n <input\n class=\"mb-radio__native\"\n type=\"radio\"\n [name]=\"name\"\n [checked]=\"value === option.value\"\n [disabled]=\"disabled || option.disabled\"\n (change)=\"choose(option)\" />\n <span class=\"mb-radio__circle\"><span class=\"mb-radio__dot\"></span></span>\n <span class=\"mb-radio__content\">\n <span class=\"mb-radio__label\">{{ option.label }}</span>\n <span class=\"mb-radio__hint\" *ngIf=\"option.hint\">{{ option.hint }}</span>\n </span>\n </label>\n</div>\n", styles: [".mb-radio-group{display:grid;gap:.7rem}.mb-radio-group--horizontal{display:flex;flex-wrap:wrap;gap:.9rem 1.4rem}.mb-radio{display:inline-flex;align-items:flex-start;gap:.55rem;cursor:pointer;-webkit-user-select:none;user-select:none}.mb-radio__native{position:absolute;opacity:0;pointer-events:none}.mb-radio__circle{inline-size:1.1rem;block-size:1.1rem;border:1px solid var(--mb-color-border-strong);border-radius:999px;background:var(--mb-color-bg);display:grid;place-items:center;margin-block-start:.08rem;transition:var(--mb-transition-fast)}.mb-radio__dot{inline-size:.52rem;block-size:.52rem;border-radius:999px;background:transparent;transform:scale(.5);transition:var(--mb-transition-fast)}.mb-radio:hover .mb-radio__circle,.mb-radio--checked .mb-radio__circle{border-color:var(--mb-color-primary)}.mb-radio--checked .mb-radio__dot{background:var(--mb-color-primary);transform:scale(1)}.mb-radio__native:focus-visible+.mb-radio__circle{box-shadow:var(--mb-focus-ring)}.mb-radio__content{display:grid;gap:.1rem}.mb-radio__label{font-weight:650;font-size:.9rem;color:var(--mb-color-text)}.mb-radio__hint{color:var(--mb-color-muted);font-size:.78rem}.mb-radio--disabled{opacity:.62;cursor:not-allowed}.mb-radio-group--sm .mb-radio__circle{inline-size:.95rem;block-size:.95rem}.mb-radio-group--lg .mb-radio__circle{inline-size:1.25rem;block-size:1.25rem}\n"] }]
2819
+ }], propDecorators: { options: [{
2820
+ type: Input
2821
+ }], name: [{
2822
+ type: Input
2823
+ }], size: [{
2824
+ type: Input
2825
+ }], direction: [{
2826
+ type: Input
2827
+ }], disabled: [{
2828
+ type: Input
2829
+ }], valueChange: [{
2830
+ type: Output
2831
+ }], hostClass: [{
2832
+ type: HostBinding,
2833
+ args: ['class']
2834
+ }] } });
2835
+
2836
+ class MbTextareaComponent {
2837
+ placeholder = '';
2838
+ rows = 4;
2839
+ size = 'md';
2840
+ disabled = false;
2841
+ readonly = false;
2842
+ invalid = false;
2843
+ autoResize = false;
2844
+ maxLength;
2845
+ valueChange = new EventEmitter();
2846
+ textarea;
2847
+ hostClass = 'mb-textarea-host';
2848
+ value = '';
2849
+ onChange = () => undefined;
2850
+ onTouched = () => undefined;
2851
+ writeValue(value) { this.value = value ?? ''; }
2852
+ registerOnChange(fn) { this.onChange = fn; }
2853
+ registerOnTouched(fn) { this.onTouched = fn; }
2854
+ setDisabledState(isDisabled) { this.disabled = isDisabled; }
2855
+ onInput(event) {
2856
+ const next = event.target.value;
2857
+ this.value = next;
2858
+ this.onChange(next);
2859
+ this.valueChange.emit(next);
2860
+ if (this.autoResize)
2861
+ this.resize();
2862
+ }
2863
+ blur() { this.onTouched(); }
2864
+ resize() {
2865
+ const el = this.textarea?.nativeElement;
2866
+ if (!el)
2867
+ return;
2868
+ el.style.height = 'auto';
2869
+ el.style.height = `${el.scrollHeight}px`;
2870
+ }
2871
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbTextareaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2872
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbTextareaComponent, isStandalone: true, selector: "mb-textarea", inputs: { placeholder: "placeholder", rows: "rows", size: "size", disabled: "disabled", readonly: "readonly", invalid: "invalid", autoResize: "autoResize", maxLength: "maxLength" }, outputs: { valueChange: "valueChange" }, host: { properties: { "class": "this.hostClass" } }, providers: [
2873
+ {
2874
+ provide: NG_VALUE_ACCESSOR,
2875
+ useExisting: forwardRef(() => MbTextareaComponent),
2876
+ multi: true,
2877
+ },
2878
+ ], viewQueries: [{ propertyName: "textarea", first: true, predicate: ["textarea"], descendants: true }], ngImport: i0, template: "<textarea\n #textarea\n class=\"mb-textarea\"\n [class.mb-textarea--invalid]=\"invalid\"\n [ngClass]=\"'mb-textarea--' + size\"\n [placeholder]=\"placeholder\"\n [rows]=\"rows\"\n [disabled]=\"disabled\"\n [readOnly]=\"readonly\"\n [maxLength]=\"maxLength ?? null\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"blur()\"></textarea>\n", styles: [":host{display:block}.mb-textarea{inline-size:100%;min-block-size:5rem;border:1px solid var(--mb-color-border);border-radius:var(--mb-radius-md);background:var(--mb-color-bg);color:var(--mb-color-text);font:inherit;padding:.72rem .85rem;resize:vertical;outline:none;transition:var(--mb-transition-fast)}.mb-textarea:focus{border-color:var(--mb-color-primary);box-shadow:var(--mb-focus-ring)}.mb-textarea::placeholder{color:var(--mb-color-muted)}.mb-textarea:disabled{opacity:.7;cursor:not-allowed;background:var(--mb-color-surface)}.mb-textarea--invalid{border-color:var(--mb-color-danger)}.mb-textarea--sm{padding:.55rem .7rem;font-size:.86rem}.mb-textarea--lg{padding:.9rem 1rem;font-size:1rem}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2879
+ }
2880
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbTextareaComponent, decorators: [{
2881
+ type: Component,
2882
+ args: [{ selector: 'mb-textarea', standalone: true, imports: [NgClass], providers: [
2883
+ {
2884
+ provide: NG_VALUE_ACCESSOR,
2885
+ useExisting: forwardRef(() => MbTextareaComponent),
2886
+ multi: true,
2887
+ },
2888
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<textarea\n #textarea\n class=\"mb-textarea\"\n [class.mb-textarea--invalid]=\"invalid\"\n [ngClass]=\"'mb-textarea--' + size\"\n [placeholder]=\"placeholder\"\n [rows]=\"rows\"\n [disabled]=\"disabled\"\n [readOnly]=\"readonly\"\n [maxLength]=\"maxLength ?? null\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"blur()\"></textarea>\n", styles: [":host{display:block}.mb-textarea{inline-size:100%;min-block-size:5rem;border:1px solid var(--mb-color-border);border-radius:var(--mb-radius-md);background:var(--mb-color-bg);color:var(--mb-color-text);font:inherit;padding:.72rem .85rem;resize:vertical;outline:none;transition:var(--mb-transition-fast)}.mb-textarea:focus{border-color:var(--mb-color-primary);box-shadow:var(--mb-focus-ring)}.mb-textarea::placeholder{color:var(--mb-color-muted)}.mb-textarea:disabled{opacity:.7;cursor:not-allowed;background:var(--mb-color-surface)}.mb-textarea--invalid{border-color:var(--mb-color-danger)}.mb-textarea--sm{padding:.55rem .7rem;font-size:.86rem}.mb-textarea--lg{padding:.9rem 1rem;font-size:1rem}\n"] }]
2889
+ }], propDecorators: { placeholder: [{
2890
+ type: Input
2891
+ }], rows: [{
2892
+ type: Input
2893
+ }], size: [{
2894
+ type: Input
2895
+ }], disabled: [{
2896
+ type: Input
2897
+ }], readonly: [{
2898
+ type: Input
2899
+ }], invalid: [{
2900
+ type: Input
2901
+ }], autoResize: [{
2902
+ type: Input
2903
+ }], maxLength: [{
2904
+ type: Input
2905
+ }], valueChange: [{
2906
+ type: Output
2907
+ }], textarea: [{
2908
+ type: ViewChild,
2909
+ args: ['textarea']
2910
+ }], hostClass: [{
2911
+ type: HostBinding,
2912
+ args: ['class']
2913
+ }] } });
2914
+
2915
+ class MbDatepickerComponent {
2916
+ cdr;
2917
+ host;
2918
+ messages = inject(MB_UI_MESSAGES);
2919
+ uiConfig = inject(MB_UI_CONFIG);
2920
+ mode = 'date';
2921
+ calendar = 'gregorian';
2922
+ placeholder;
2923
+ min;
2924
+ max;
2925
+ size = 'md';
2926
+ disabled = false;
2927
+ invalid = false;
2928
+ clearable = true;
2929
+ firstDayOfWeek = 0;
2930
+ locale;
2931
+ monthNames;
2932
+ weekdayNames;
2933
+ disabledDates = () => false;
2934
+ valueChange = new EventEmitter();
2935
+ openedChange = new EventEmitter();
2936
+ hostClass = 'mb-datepicker-host';
2937
+ value = '';
2938
+ opened = false;
2939
+ viewDate = this.startOfMonth(new Date());
2940
+ viewJalali = mbGregorianToJalali(this.toGregorian(new Date()));
2941
+ days = [];
2942
+ years = Array.from({ length: 21 }, (_, index) => new Date().getFullYear() - 10 + index);
2943
+ jalaliYears = Array.from({ length: 21 }, (_, index) => this.viewJalali.year - 10 + index);
2944
+ onChange = () => undefined;
2945
+ onTouched = () => undefined;
2946
+ constructor(cdr, host) {
2947
+ this.cdr = cdr;
2948
+ this.host = host;
2949
+ this.rebuildCalendar();
2950
+ }
2951
+ get effectivePlaceholder() {
2952
+ return this.placeholder ?? this.messages.datepicker.placeholder;
2953
+ }
2954
+ get effectiveLocale() {
2955
+ return this.locale ?? this.uiConfig.locale;
2956
+ }
2957
+ get effectiveWeekdayNames() {
2958
+ return this.weekdayNames ?? this.messages.datepicker.weekdayNames;
2959
+ }
2960
+ get effectiveGregorianMonthNames() {
2961
+ return this.monthNames ?? this.messages.datepicker.monthNamesGregorian;
2962
+ }
2963
+ get activeMonthNames() {
2964
+ return this.calendar === 'jalali' ? MB_JALALI_MONTH_NAMES : this.effectiveGregorianMonthNames;
2965
+ }
2966
+ get activeYears() {
2967
+ if (this.calendar === 'jalali') {
2968
+ const center = this.viewJalali.year;
2969
+ return Array.from({ length: 21 }, (_, index) => center - 10 + index);
2970
+ }
2971
+ return this.years;
2972
+ }
2973
+ onDocumentClick(event) {
2974
+ if (this.opened && !this.host.nativeElement.contains(event.target)) {
2975
+ this.close();
2976
+ }
2977
+ }
2978
+ writeValue(value) {
2979
+ if (value instanceof Date) {
2980
+ this.value = this.toIso(value);
2981
+ this.viewDate = this.startOfMonth(value);
2982
+ this.viewJalali = mbGregorianToJalali(this.toGregorian(value));
2983
+ }
2984
+ else {
2985
+ this.value = value ?? '';
2986
+ const parsed = this.parseIso(this.value);
2987
+ if (parsed) {
2988
+ this.viewDate = this.startOfMonth(parsed);
2989
+ this.viewJalali = mbGregorianToJalali(this.toGregorian(parsed));
2990
+ }
2991
+ }
2992
+ this.rebuildCalendar();
2993
+ this.cdr.markForCheck();
2994
+ }
2995
+ registerOnChange(fn) {
2996
+ this.onChange = fn;
2997
+ }
2998
+ registerOnTouched(fn) {
2999
+ this.onTouched = fn;
3000
+ }
3001
+ setDisabledState(isDisabled) {
3002
+ this.disabled = isDisabled;
3003
+ this.cdr.markForCheck();
3004
+ }
3005
+ toggle() {
3006
+ if (!this.disabled) {
3007
+ this.opened ? this.close() : this.open();
3008
+ }
3009
+ }
3010
+ open() {
3011
+ if (this.disabled)
3012
+ return;
3013
+ this.opened = true;
3014
+ this.openedChange.emit(true);
3015
+ this.rebuildCalendar();
3016
+ this.cdr.markForCheck();
3017
+ }
3018
+ close() {
3019
+ this.opened = false;
3020
+ this.onTouched();
3021
+ this.openedChange.emit(false);
3022
+ this.cdr.markForCheck();
3023
+ }
3024
+ clear(event) {
3025
+ event?.stopPropagation();
3026
+ if (this.disabled)
3027
+ return;
3028
+ this.commit('');
3029
+ this.close();
3030
+ }
3031
+ select(day) {
3032
+ if (day.disabled)
3033
+ return;
3034
+ this.commit(day.iso);
3035
+ this.close();
3036
+ }
3037
+ selectToday() {
3038
+ const today = new Date();
3039
+ if (!this.isDisabled(today)) {
3040
+ this.viewDate = this.startOfMonth(today);
3041
+ this.viewJalali = mbGregorianToJalali(this.toGregorian(today));
3042
+ this.commit(this.toIso(today));
3043
+ this.close();
3044
+ }
3045
+ }
3046
+ prevMonth() {
3047
+ if (this.calendar === 'jalali') {
3048
+ const month = this.viewJalali.month === 1 ? 12 : this.viewJalali.month - 1;
3049
+ const year = this.viewJalali.month === 1 ? this.viewJalali.year - 1 : this.viewJalali.year;
3050
+ this.viewJalali = { year, month, day: 1 };
3051
+ this.viewDate = this.startOfMonth(this.jalaliToDate(this.viewJalali));
3052
+ }
3053
+ else {
3054
+ this.viewDate = new Date(this.viewDate.getFullYear(), this.viewDate.getMonth() - 1, 1);
3055
+ }
3056
+ this.rebuildCalendar();
3057
+ }
3058
+ nextMonth() {
3059
+ if (this.calendar === 'jalali') {
3060
+ const month = this.viewJalali.month === 12 ? 1 : this.viewJalali.month + 1;
3061
+ const year = this.viewJalali.month === 12 ? this.viewJalali.year + 1 : this.viewJalali.year;
3062
+ this.viewJalali = { year, month, day: 1 };
3063
+ this.viewDate = this.startOfMonth(this.jalaliToDate(this.viewJalali));
3064
+ }
3065
+ else {
3066
+ this.viewDate = new Date(this.viewDate.getFullYear(), this.viewDate.getMonth() + 1, 1);
3067
+ }
3068
+ this.rebuildCalendar();
3069
+ }
3070
+ setMonth(value) {
3071
+ const month = Number(value);
3072
+ if (this.calendar === 'jalali') {
3073
+ this.viewJalali = { ...this.viewJalali, month: month + 1 };
3074
+ this.viewDate = this.startOfMonth(this.jalaliToDate(this.viewJalali));
3075
+ if (this.mode === 'month') {
3076
+ const gregorian = mbJalaliToGregorian({ year: this.viewJalali.year, month: this.viewJalali.month, day: 1 });
3077
+ this.commit(`${gregorian.year}-${String(gregorian.month).padStart(2, '0')}`);
3078
+ this.close();
3079
+ return;
3080
+ }
3081
+ }
3082
+ else {
3083
+ this.viewDate = new Date(this.viewDate.getFullYear(), month, 1);
3084
+ if (this.mode === 'month') {
3085
+ this.commit(`${this.viewDate.getFullYear()}-${String(month + 1).padStart(2, '0')}`);
3086
+ this.close();
3087
+ return;
3088
+ }
3089
+ }
3090
+ this.rebuildCalendar();
3091
+ }
3092
+ setYear(value) {
3093
+ const year = Number(value);
3094
+ if (this.calendar === 'jalali') {
3095
+ this.viewJalali = { ...this.viewJalali, year };
3096
+ this.viewDate = this.startOfMonth(this.jalaliToDate(this.viewJalali));
3097
+ }
3098
+ else {
3099
+ this.viewDate = new Date(year, this.viewDate.getMonth(), 1);
3100
+ }
3101
+ this.rebuildCalendar();
3102
+ }
3103
+ displayValue() {
3104
+ if (!this.value)
3105
+ return '';
3106
+ if (this.mode === 'month') {
3107
+ if (this.calendar === 'jalali') {
3108
+ const parsed = mbParseIsoDate(this.value.length === 7 ? `${this.value}-01` : this.value);
3109
+ if (!parsed)
3110
+ return this.value;
3111
+ const jalali = mbGregorianToJalali(parsed);
3112
+ return `${MB_JALALI_MONTH_NAMES[jalali.month - 1]} ${jalali.year}`;
3113
+ }
3114
+ const [year, month] = this.value.split('-');
3115
+ return `${this.monthNames[Number(month) - 1] ?? month} ${year}`;
3116
+ }
3117
+ const parsed = this.parseIso(this.value);
3118
+ if (!parsed)
3119
+ return this.value;
3120
+ if (this.calendar === 'jalali') {
3121
+ return mbFormatJalali(mbGregorianToJalali(this.toGregorian(parsed)));
3122
+ }
3123
+ try {
3124
+ return new Intl.DateTimeFormat(this.effectiveLocale, { dateStyle: 'medium' }).format(parsed);
3125
+ }
3126
+ catch {
3127
+ return this.value;
3128
+ }
3129
+ }
3130
+ currentMonthIndex() {
3131
+ return this.calendar === 'jalali' ? this.viewJalali.month - 1 : this.viewDate.getMonth();
3132
+ }
3133
+ currentYear() {
3134
+ return this.calendar === 'jalali' ? this.viewJalali.year : this.viewDate.getFullYear();
3135
+ }
3136
+ commit(value, emit = true) {
3137
+ this.value = value;
3138
+ this.onChange(value);
3139
+ if (emit)
3140
+ this.valueChange.emit(value);
3141
+ this.rebuildCalendar();
3142
+ this.cdr.markForCheck();
3143
+ }
3144
+ rebuildCalendar() {
3145
+ this.days = this.calendar === 'jalali' ? this.buildJalaliCalendar() : this.buildGregorianCalendar();
3146
+ this.cdr.markForCheck();
3147
+ }
3148
+ buildGregorianCalendar() {
3149
+ const first = this.startOfMonth(this.viewDate);
3150
+ const start = new Date(first);
3151
+ const offset = (start.getDay() - this.firstDayOfWeek + 7) % 7;
3152
+ start.setDate(start.getDate() - offset);
3153
+ const selected = this.parseIso(this.value);
3154
+ const todayIso = this.toIso(new Date());
3155
+ return Array.from({ length: 42 }, (_, index) => {
3156
+ const date = new Date(start);
3157
+ date.setDate(start.getDate() + index);
3158
+ const iso = this.toIso(date);
3159
+ return {
3160
+ date,
3161
+ iso,
3162
+ label: date.getDate(),
3163
+ currentMonth: date.getMonth() === first.getMonth(),
3164
+ today: iso === todayIso,
3165
+ selected: !!selected && iso === this.toIso(selected),
3166
+ disabled: this.isDisabled(date),
3167
+ };
3168
+ });
3169
+ }
3170
+ buildJalaliCalendar() {
3171
+ const month = this.viewJalali.month;
3172
+ const year = this.viewJalali.year;
3173
+ const firstGregorian = this.jalaliToDate({ year, month, day: 1 });
3174
+ const start = new Date(firstGregorian);
3175
+ const offset = (start.getDay() - this.firstDayOfWeek + 7) % 7;
3176
+ start.setDate(start.getDate() - offset);
3177
+ const selected = this.parseIso(this.value);
3178
+ const todayIso = this.toIso(new Date());
3179
+ return Array.from({ length: 42 }, (_, index) => {
3180
+ const date = new Date(start);
3181
+ date.setDate(start.getDate() + index);
3182
+ const jalali = mbGregorianToJalali(this.toGregorian(date));
3183
+ const iso = this.toIso(date);
3184
+ return {
3185
+ date,
3186
+ iso,
3187
+ label: jalali.day,
3188
+ jalali,
3189
+ currentMonth: jalali.month === month && jalali.year === year,
3190
+ today: iso === todayIso,
3191
+ selected: !!selected && iso === this.toIso(selected),
3192
+ disabled: this.isDisabled(date),
3193
+ };
3194
+ });
3195
+ }
3196
+ jalaliToDate(date) {
3197
+ const gregorian = mbJalaliToGregorian(date);
3198
+ return new Date(gregorian.year, gregorian.month - 1, gregorian.day);
3199
+ }
3200
+ toGregorian(date) {
3201
+ return { year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate() };
3202
+ }
3203
+ isDisabled(date) {
3204
+ const iso = this.toIso(date);
3205
+ if (this.min && iso < this.min)
3206
+ return true;
3207
+ if (this.max && iso > this.max)
3208
+ return true;
3209
+ if (this.calendar === 'jalali') {
3210
+ const jalali = mbGregorianToJalali(this.toGregorian(date));
3211
+ const monthLength = mbJalaliMonthLength(jalali.year, jalali.month);
3212
+ if (jalali.day > monthLength)
3213
+ return true;
3214
+ }
3215
+ return this.disabledDates(date);
3216
+ }
3217
+ parseIso(value) {
3218
+ if (!value)
3219
+ return null;
3220
+ const parts = value.split('-').map(Number);
3221
+ if (parts.length < 2 || parts.some(Number.isNaN))
3222
+ return null;
3223
+ const [year, month, day = 1] = parts;
3224
+ return new Date(year, month - 1, day);
3225
+ }
3226
+ toIso(date) {
3227
+ return mbToIsoDate(this.toGregorian(date));
3228
+ }
3229
+ startOfMonth(date) {
3230
+ return new Date(date.getFullYear(), date.getMonth(), 1);
3231
+ }
3232
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbDatepickerComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
3233
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbDatepickerComponent, isStandalone: true, selector: "mb-datepicker", inputs: { mode: "mode", calendar: "calendar", placeholder: "placeholder", min: "min", max: "max", size: "size", disabled: "disabled", invalid: "invalid", clearable: "clearable", firstDayOfWeek: "firstDayOfWeek", locale: "locale", monthNames: "monthNames", weekdayNames: "weekdayNames", disabledDates: "disabledDates" }, outputs: { valueChange: "valueChange", openedChange: "openedChange" }, host: { listeners: { "document:click": "onDocumentClick($event)" }, properties: { "class": "this.hostClass" } }, providers: [
3234
+ {
3235
+ provide: NG_VALUE_ACCESSOR,
3236
+ useExisting: forwardRef(() => MbDatepickerComponent),
3237
+ multi: true,
3238
+ },
3239
+ ], ngImport: i0, template: "<div class=\"mb-datepicker-shell\" [class.mb-datepicker-shell--open]=\"opened\">\n <button\n type=\"button\"\n class=\"mb-datepicker-trigger\"\n [class.mb-datepicker-trigger--invalid]=\"invalid\"\n [ngClass]=\"'mb-datepicker-trigger--' + size\"\n [disabled]=\"disabled\"\n (click)=\"toggle()\">\n <span class=\"mb-datepicker-value\" [class.mb-datepicker-value--placeholder]=\"!value\">{{ value ? displayValue() : effectivePlaceholder }}</span>\n <span class=\"mb-datepicker-actions\">\n <button\n *ngIf=\"clearable && value && !disabled\"\n type=\"button\"\n class=\"mb-datepicker-clear\"\n [attr.aria-label]=\"messages.datepicker.clear\"\n (click)=\"clear($event)\">\u00D7</button>\n <span class=\"mb-datepicker-icon\" aria-hidden=\"true\">\uD83D\uDCC5</span>\n </span>\n </button>\n\n <div *ngIf=\"opened\" class=\"mb-datepicker-panel\" role=\"dialog\" [attr.aria-label]=\"messages.datepicker.pickDate\">\n <div class=\"mb-datepicker-header\">\n <button type=\"button\" class=\"mb-datepicker-nav\" (click)=\"prevMonth()\">\u2039</button>\n <div class=\"mb-datepicker-title\">\n <select [value]=\"currentMonthIndex()\" (change)=\"setMonth($any($event.target).value)\">\n <option *ngFor=\"let month of activeMonthNames; index as monthIndex\" [value]=\"monthIndex\">{{ month }}</option>\n </select>\n <select [value]=\"currentYear()\" (change)=\"setYear($any($event.target).value)\">\n <option *ngFor=\"let year of activeYears\" [value]=\"year\">{{ year }}</option>\n </select>\n </div>\n <button type=\"button\" class=\"mb-datepicker-nav\" (click)=\"nextMonth()\">\u203A</button>\n </div>\n\n <ng-container *ngIf=\"mode === 'date'; else monthPicker\">\n <div class=\"mb-datepicker-weekdays\">\n <span *ngFor=\"let weekday of effectiveWeekdayNames\">{{ weekday }}</span>\n </div>\n <div class=\"mb-datepicker-grid\">\n <button\n *ngFor=\"let day of days\"\n type=\"button\"\n class=\"mb-datepicker-day\"\n [class.mb-datepicker-day--muted]=\"!day.currentMonth\"\n [class.mb-datepicker-day--today]=\"day.today\"\n [class.mb-datepicker-day--selected]=\"day.selected\"\n [disabled]=\"day.disabled\"\n (click)=\"select(day)\">{{ day.label }}</button>\n </div>\n </ng-container>\n\n <ng-template #monthPicker>\n <div class=\"mb-datepicker-months\">\n <button *ngFor=\"let month of activeMonthNames; index as monthIndex\" type=\"button\" class=\"mb-datepicker-month\" (click)=\"setMonth(monthIndex)\">{{ month }}</button>\n </div>\n </ng-template>\n\n <div class=\"mb-datepicker-footer\">\n <button type=\"button\" (click)=\"selectToday()\">{{ messages.datepicker.today }}</button>\n <button type=\"button\" (click)=\"close()\">{{ messages.common.close }}</button>\n </div>\n </div>\n</div>\n", styles: [":host{display:inline-block;min-width:14rem;position:relative}.mb-datepicker-shell{position:relative;width:100%}.mb-datepicker-trigger{width:100%;min-height:var(--mb-control-height-md, 2.5rem);display:inline-flex;align-items:center;justify-content:space-between;gap:.5rem;border:1px solid var(--mb-color-border, #d0d5dd);border-radius:var(--mb-radius-md, .625rem);padding-inline:.75rem;background:var(--mb-color-surface, #fff);color:var(--mb-color-text, #101828);cursor:pointer}.mb-datepicker-trigger:hover{border-color:var(--mb-color-primary, #2563eb)}.mb-datepicker-trigger--invalid{border-color:var(--mb-color-danger, #dc2626)}.mb-datepicker-value--placeholder{color:var(--mb-color-muted, #667085)}.mb-datepicker-actions{display:inline-flex;align-items:center;gap:.25rem}.mb-datepicker-clear{border:0;background:transparent;color:var(--mb-color-muted, #667085);cursor:pointer;font-size:1.2rem}.mb-datepicker-panel{position:absolute;z-index:1000;inset-block-start:calc(100% + .4rem);inset-inline-start:0;inline-size:20rem;max-inline-size:min(20rem,100vw - 2rem);border:1px solid var(--mb-color-border, #e2e8f0);border-radius:var(--mb-radius-lg, .875rem);background:var(--mb-color-surface, #fff);color:var(--mb-color-text, #101828);box-shadow:var(--mb-shadow-lg, 0 18px 45px rgba(15, 23, 42, .16));padding:.75rem}.mb-datepicker-header{display:flex;align-items:center;justify-content:space-between;gap:.5rem;margin-block-end:.75rem}.mb-datepicker-nav,.mb-datepicker-footer button,.mb-datepicker-month,.mb-datepicker-day{border:1px solid transparent;border-radius:var(--mb-radius-md, .625rem);background:transparent;color:inherit;cursor:pointer}.mb-datepicker-nav{inline-size:2rem;block-size:2rem;font-size:1.4rem}.mb-datepicker-nav:hover,.mb-datepicker-footer button:hover,.mb-datepicker-month:hover,.mb-datepicker-day:hover:not(:disabled){background:var(--mb-color-surface-muted, #f1f5f9)}.mb-datepicker-title{display:flex;gap:.4rem}.mb-datepicker-title select{border:1px solid var(--mb-color-border, #e2e8f0);border-radius:var(--mb-radius-sm, .5rem);background:var(--mb-color-surface, #fff);color:inherit;padding:.25rem .45rem}.mb-datepicker-weekdays,.mb-datepicker-grid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:.25rem}.mb-datepicker-weekdays{color:var(--mb-color-muted, #64748b);font-size:.75rem;text-align:center;margin-block-end:.25rem}.mb-datepicker-day{aspect-ratio:1;font-variant-numeric:tabular-nums}.mb-datepicker-day--muted{color:var(--mb-color-muted, #64748b);opacity:.65}.mb-datepicker-day--today{border-color:var(--mb-color-primary, #2563eb)}.mb-datepicker-day--selected{background:var(--mb-color-primary, #2563eb);color:var(--mb-color-primary-contrast, #fff)}.mb-datepicker-day:disabled{opacity:.35;cursor:not-allowed}.mb-datepicker-months{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.35rem}.mb-datepicker-month{padding:.6rem .35rem}.mb-datepicker-footer{display:flex;justify-content:space-between;gap:.5rem;margin-block-start:.75rem;border-block-start:1px solid var(--mb-color-border, #e2e8f0);padding-block-start:.75rem}.mb-datepicker-footer button{padding:.45rem .75rem}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3240
+ }
3241
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbDatepickerComponent, decorators: [{
3242
+ type: Component,
3243
+ args: [{ selector: 'mb-datepicker', standalone: true, imports: [NgClass, NgFor, NgIf], providers: [
3244
+ {
3245
+ provide: NG_VALUE_ACCESSOR,
3246
+ useExisting: forwardRef(() => MbDatepickerComponent),
3247
+ multi: true,
3248
+ },
3249
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mb-datepicker-shell\" [class.mb-datepicker-shell--open]=\"opened\">\n <button\n type=\"button\"\n class=\"mb-datepicker-trigger\"\n [class.mb-datepicker-trigger--invalid]=\"invalid\"\n [ngClass]=\"'mb-datepicker-trigger--' + size\"\n [disabled]=\"disabled\"\n (click)=\"toggle()\">\n <span class=\"mb-datepicker-value\" [class.mb-datepicker-value--placeholder]=\"!value\">{{ value ? displayValue() : effectivePlaceholder }}</span>\n <span class=\"mb-datepicker-actions\">\n <button\n *ngIf=\"clearable && value && !disabled\"\n type=\"button\"\n class=\"mb-datepicker-clear\"\n [attr.aria-label]=\"messages.datepicker.clear\"\n (click)=\"clear($event)\">\u00D7</button>\n <span class=\"mb-datepicker-icon\" aria-hidden=\"true\">\uD83D\uDCC5</span>\n </span>\n </button>\n\n <div *ngIf=\"opened\" class=\"mb-datepicker-panel\" role=\"dialog\" [attr.aria-label]=\"messages.datepicker.pickDate\">\n <div class=\"mb-datepicker-header\">\n <button type=\"button\" class=\"mb-datepicker-nav\" (click)=\"prevMonth()\">\u2039</button>\n <div class=\"mb-datepicker-title\">\n <select [value]=\"currentMonthIndex()\" (change)=\"setMonth($any($event.target).value)\">\n <option *ngFor=\"let month of activeMonthNames; index as monthIndex\" [value]=\"monthIndex\">{{ month }}</option>\n </select>\n <select [value]=\"currentYear()\" (change)=\"setYear($any($event.target).value)\">\n <option *ngFor=\"let year of activeYears\" [value]=\"year\">{{ year }}</option>\n </select>\n </div>\n <button type=\"button\" class=\"mb-datepicker-nav\" (click)=\"nextMonth()\">\u203A</button>\n </div>\n\n <ng-container *ngIf=\"mode === 'date'; else monthPicker\">\n <div class=\"mb-datepicker-weekdays\">\n <span *ngFor=\"let weekday of effectiveWeekdayNames\">{{ weekday }}</span>\n </div>\n <div class=\"mb-datepicker-grid\">\n <button\n *ngFor=\"let day of days\"\n type=\"button\"\n class=\"mb-datepicker-day\"\n [class.mb-datepicker-day--muted]=\"!day.currentMonth\"\n [class.mb-datepicker-day--today]=\"day.today\"\n [class.mb-datepicker-day--selected]=\"day.selected\"\n [disabled]=\"day.disabled\"\n (click)=\"select(day)\">{{ day.label }}</button>\n </div>\n </ng-container>\n\n <ng-template #monthPicker>\n <div class=\"mb-datepicker-months\">\n <button *ngFor=\"let month of activeMonthNames; index as monthIndex\" type=\"button\" class=\"mb-datepicker-month\" (click)=\"setMonth(monthIndex)\">{{ month }}</button>\n </div>\n </ng-template>\n\n <div class=\"mb-datepicker-footer\">\n <button type=\"button\" (click)=\"selectToday()\">{{ messages.datepicker.today }}</button>\n <button type=\"button\" (click)=\"close()\">{{ messages.common.close }}</button>\n </div>\n </div>\n</div>\n", styles: [":host{display:inline-block;min-width:14rem;position:relative}.mb-datepicker-shell{position:relative;width:100%}.mb-datepicker-trigger{width:100%;min-height:var(--mb-control-height-md, 2.5rem);display:inline-flex;align-items:center;justify-content:space-between;gap:.5rem;border:1px solid var(--mb-color-border, #d0d5dd);border-radius:var(--mb-radius-md, .625rem);padding-inline:.75rem;background:var(--mb-color-surface, #fff);color:var(--mb-color-text, #101828);cursor:pointer}.mb-datepicker-trigger:hover{border-color:var(--mb-color-primary, #2563eb)}.mb-datepicker-trigger--invalid{border-color:var(--mb-color-danger, #dc2626)}.mb-datepicker-value--placeholder{color:var(--mb-color-muted, #667085)}.mb-datepicker-actions{display:inline-flex;align-items:center;gap:.25rem}.mb-datepicker-clear{border:0;background:transparent;color:var(--mb-color-muted, #667085);cursor:pointer;font-size:1.2rem}.mb-datepicker-panel{position:absolute;z-index:1000;inset-block-start:calc(100% + .4rem);inset-inline-start:0;inline-size:20rem;max-inline-size:min(20rem,100vw - 2rem);border:1px solid var(--mb-color-border, #e2e8f0);border-radius:var(--mb-radius-lg, .875rem);background:var(--mb-color-surface, #fff);color:var(--mb-color-text, #101828);box-shadow:var(--mb-shadow-lg, 0 18px 45px rgba(15, 23, 42, .16));padding:.75rem}.mb-datepicker-header{display:flex;align-items:center;justify-content:space-between;gap:.5rem;margin-block-end:.75rem}.mb-datepicker-nav,.mb-datepicker-footer button,.mb-datepicker-month,.mb-datepicker-day{border:1px solid transparent;border-radius:var(--mb-radius-md, .625rem);background:transparent;color:inherit;cursor:pointer}.mb-datepicker-nav{inline-size:2rem;block-size:2rem;font-size:1.4rem}.mb-datepicker-nav:hover,.mb-datepicker-footer button:hover,.mb-datepicker-month:hover,.mb-datepicker-day:hover:not(:disabled){background:var(--mb-color-surface-muted, #f1f5f9)}.mb-datepicker-title{display:flex;gap:.4rem}.mb-datepicker-title select{border:1px solid var(--mb-color-border, #e2e8f0);border-radius:var(--mb-radius-sm, .5rem);background:var(--mb-color-surface, #fff);color:inherit;padding:.25rem .45rem}.mb-datepicker-weekdays,.mb-datepicker-grid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:.25rem}.mb-datepicker-weekdays{color:var(--mb-color-muted, #64748b);font-size:.75rem;text-align:center;margin-block-end:.25rem}.mb-datepicker-day{aspect-ratio:1;font-variant-numeric:tabular-nums}.mb-datepicker-day--muted{color:var(--mb-color-muted, #64748b);opacity:.65}.mb-datepicker-day--today{border-color:var(--mb-color-primary, #2563eb)}.mb-datepicker-day--selected{background:var(--mb-color-primary, #2563eb);color:var(--mb-color-primary-contrast, #fff)}.mb-datepicker-day:disabled{opacity:.35;cursor:not-allowed}.mb-datepicker-months{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.35rem}.mb-datepicker-month{padding:.6rem .35rem}.mb-datepicker-footer{display:flex;justify-content:space-between;gap:.5rem;margin-block-start:.75rem;border-block-start:1px solid var(--mb-color-border, #e2e8f0);padding-block-start:.75rem}.mb-datepicker-footer button{padding:.45rem .75rem}\n"] }]
3250
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }], propDecorators: { mode: [{
3251
+ type: Input
3252
+ }], calendar: [{
3253
+ type: Input
3254
+ }], placeholder: [{
3255
+ type: Input
3256
+ }], min: [{
3257
+ type: Input
3258
+ }], max: [{
3259
+ type: Input
3260
+ }], size: [{
3261
+ type: Input
3262
+ }], disabled: [{
3263
+ type: Input
3264
+ }], invalid: [{
3265
+ type: Input
3266
+ }], clearable: [{
3267
+ type: Input
3268
+ }], firstDayOfWeek: [{
3269
+ type: Input
3270
+ }], locale: [{
3271
+ type: Input
3272
+ }], monthNames: [{
3273
+ type: Input
3274
+ }], weekdayNames: [{
3275
+ type: Input
3276
+ }], disabledDates: [{
3277
+ type: Input
3278
+ }], valueChange: [{
3279
+ type: Output
3280
+ }], openedChange: [{
3281
+ type: Output
3282
+ }], hostClass: [{
3283
+ type: HostBinding,
3284
+ args: ['class']
3285
+ }], onDocumentClick: [{
3286
+ type: HostListener,
3287
+ args: ['document:click', ['$event']]
3288
+ }] } });
3289
+
3290
+ class MbBadgeComponent {
3291
+ variant = 'neutral';
3292
+ size = 'md';
3293
+ dot = false;
3294
+ pill = true;
3295
+ hostClass = 'mb-badge-host';
3296
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3297
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbBadgeComponent, isStandalone: true, selector: "mb-badge", inputs: { variant: "variant", size: "size", dot: "dot", pill: "pill" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<span class=\"mb-badge\" [class.mb-badge--pill]=\"pill\" [class.mb-badge--dot-only]=\"dot\" [ngClass]=\"['mb-badge--' + variant, 'mb-badge--' + size]\">\n <span class=\"mb-badge__dot\" *ngIf=\"dot\"></span>\n <ng-content></ng-content>\n</span>\n", styles: [":host{display:inline-flex}.mb-badge{display:inline-flex;align-items:center;gap:.35rem;border:1px solid transparent;border-radius:var(--mb-radius-md);padding:.2rem .55rem;font-weight:750;font-size:.76rem;line-height:1.1}.mb-badge--pill{border-radius:999px}.mb-badge--sm{font-size:.68rem;padding:.14rem .42rem}.mb-badge--lg{font-size:.86rem;padding:.28rem .7rem}.mb-badge__dot{inline-size:.42rem;block-size:.42rem;border-radius:999px;background:currentColor}.mb-badge--dot-only{padding:.25rem}.mb-badge--primary{color:var(--mb-color-primary);background:var(--mb-color-primary-soft)}.mb-badge--success{color:var(--mb-color-success);background:var(--mb-color-success-soft)}.mb-badge--warning{color:var(--mb-color-warning);background:var(--mb-color-warning-soft)}.mb-badge--danger{color:var(--mb-color-danger);background:var(--mb-color-danger-soft)}.mb-badge--neutral{color:var(--mb-color-muted);background:var(--mb-color-surface);border-color:var(--mb-color-border)}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3298
+ }
3299
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbBadgeComponent, decorators: [{
3300
+ type: Component,
3301
+ args: [{ selector: 'mb-badge', standalone: true, imports: [NgClass, NgIf], changeDetection: ChangeDetectionStrategy.OnPush, template: "<span class=\"mb-badge\" [class.mb-badge--pill]=\"pill\" [class.mb-badge--dot-only]=\"dot\" [ngClass]=\"['mb-badge--' + variant, 'mb-badge--' + size]\">\n <span class=\"mb-badge__dot\" *ngIf=\"dot\"></span>\n <ng-content></ng-content>\n</span>\n", styles: [":host{display:inline-flex}.mb-badge{display:inline-flex;align-items:center;gap:.35rem;border:1px solid transparent;border-radius:var(--mb-radius-md);padding:.2rem .55rem;font-weight:750;font-size:.76rem;line-height:1.1}.mb-badge--pill{border-radius:999px}.mb-badge--sm{font-size:.68rem;padding:.14rem .42rem}.mb-badge--lg{font-size:.86rem;padding:.28rem .7rem}.mb-badge__dot{inline-size:.42rem;block-size:.42rem;border-radius:999px;background:currentColor}.mb-badge--dot-only{padding:.25rem}.mb-badge--primary{color:var(--mb-color-primary);background:var(--mb-color-primary-soft)}.mb-badge--success{color:var(--mb-color-success);background:var(--mb-color-success-soft)}.mb-badge--warning{color:var(--mb-color-warning);background:var(--mb-color-warning-soft)}.mb-badge--danger{color:var(--mb-color-danger);background:var(--mb-color-danger-soft)}.mb-badge--neutral{color:var(--mb-color-muted);background:var(--mb-color-surface);border-color:var(--mb-color-border)}\n"] }]
3302
+ }], propDecorators: { variant: [{
3303
+ type: Input
3304
+ }], size: [{
3305
+ type: Input
3306
+ }], dot: [{
3307
+ type: Input
3308
+ }], pill: [{
3309
+ type: Input
3310
+ }], hostClass: [{
3311
+ type: HostBinding,
3312
+ args: ['class']
3313
+ }] } });
3314
+
3315
+ class MbChipComponent {
3316
+ removable = false;
3317
+ selected = false;
3318
+ disabled = false;
3319
+ removed = new EventEmitter();
3320
+ hostClass = 'mb-chip-host';
3321
+ remove(event) {
3322
+ event.stopPropagation();
3323
+ if (!this.disabled)
3324
+ this.removed.emit();
3325
+ }
3326
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbChipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3327
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbChipComponent, isStandalone: true, selector: "mb-chip", inputs: { removable: "removable", selected: "selected", disabled: "disabled" }, outputs: { removed: "removed" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<span class=\"mb-chip\" [class.mb-chip--selected]=\"selected\" [class.mb-chip--disabled]=\"disabled\">\n <ng-content></ng-content>\n <button *ngIf=\"removable\" type=\"button\" class=\"mb-chip__remove\" [disabled]=\"disabled\" (click)=\"remove($event)\" aria-label=\"Remove\">\u00D7</button>\n</span>\n", styles: [":host{display:inline-flex}.mb-chip{display:inline-flex;align-items:center;gap:.45rem;padding:.35rem .65rem;border-radius:999px;background:var(--mb-color-surface);border:1px solid var(--mb-color-border);color:var(--mb-color-text);font-weight:650;font-size:.82rem}.mb-chip--selected{background:var(--mb-color-primary-soft);color:var(--mb-color-primary);border-color:transparent}.mb-chip--disabled{opacity:.6}.mb-chip__remove{border:0;background:transparent;color:inherit;cursor:pointer;font-size:1rem;line-height:1;padding:0}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3328
+ }
3329
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbChipComponent, decorators: [{
3330
+ type: Component,
3331
+ args: [{ selector: 'mb-chip', standalone: true, imports: [NgIf], changeDetection: ChangeDetectionStrategy.OnPush, template: "<span class=\"mb-chip\" [class.mb-chip--selected]=\"selected\" [class.mb-chip--disabled]=\"disabled\">\n <ng-content></ng-content>\n <button *ngIf=\"removable\" type=\"button\" class=\"mb-chip__remove\" [disabled]=\"disabled\" (click)=\"remove($event)\" aria-label=\"Remove\">\u00D7</button>\n</span>\n", styles: [":host{display:inline-flex}.mb-chip{display:inline-flex;align-items:center;gap:.45rem;padding:.35rem .65rem;border-radius:999px;background:var(--mb-color-surface);border:1px solid var(--mb-color-border);color:var(--mb-color-text);font-weight:650;font-size:.82rem}.mb-chip--selected{background:var(--mb-color-primary-soft);color:var(--mb-color-primary);border-color:transparent}.mb-chip--disabled{opacity:.6}.mb-chip__remove{border:0;background:transparent;color:inherit;cursor:pointer;font-size:1rem;line-height:1;padding:0}\n"] }]
3332
+ }], propDecorators: { removable: [{
3333
+ type: Input
3334
+ }], selected: [{
3335
+ type: Input
3336
+ }], disabled: [{
3337
+ type: Input
3338
+ }], removed: [{
3339
+ type: Output
3340
+ }], hostClass: [{
3341
+ type: HostBinding,
3342
+ args: ['class']
3343
+ }] } });
3344
+
3345
+ class MbAvatarComponent {
3346
+ src;
3347
+ alt = '';
3348
+ name = '';
3349
+ size = 'md';
3350
+ hostClass = 'mb-avatar-host';
3351
+ get initials() {
3352
+ const parts = this.name.trim().split(/\s+/).filter(Boolean);
3353
+ return parts.slice(0, 2).map((p) => p[0]?.toUpperCase() ?? '').join('') || '?';
3354
+ }
3355
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbAvatarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3356
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbAvatarComponent, isStandalone: true, selector: "mb-avatar", inputs: { src: "src", alt: "alt", name: "name", size: "size" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<span class=\"mb-avatar\" [class]=\"'mb-avatar mb-avatar--' + size\">\n <img *ngIf=\"src; else fallback\" [src]=\"src\" [alt]=\"alt || name\" />\n <ng-template #fallback>{{ initials }}</ng-template>\n</span>\n", styles: [":host{display:inline-flex}.mb-avatar{inline-size:2.3rem;block-size:2.3rem;border-radius:999px;overflow:hidden;display:inline-grid;place-items:center;background:var(--mb-color-primary-soft);color:var(--mb-color-primary);font-weight:800;border:1px solid var(--mb-color-border)}.mb-avatar img{inline-size:100%;block-size:100%;object-fit:cover}.mb-avatar--sm{inline-size:1.75rem;block-size:1.75rem;font-size:.75rem}.mb-avatar--lg{inline-size:3rem;block-size:3rem;font-size:1.1rem}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3357
+ }
3358
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbAvatarComponent, decorators: [{
3359
+ type: Component,
3360
+ args: [{ selector: 'mb-avatar', standalone: true, imports: [NgIf], changeDetection: ChangeDetectionStrategy.OnPush, template: "<span class=\"mb-avatar\" [class]=\"'mb-avatar mb-avatar--' + size\">\n <img *ngIf=\"src; else fallback\" [src]=\"src\" [alt]=\"alt || name\" />\n <ng-template #fallback>{{ initials }}</ng-template>\n</span>\n", styles: [":host{display:inline-flex}.mb-avatar{inline-size:2.3rem;block-size:2.3rem;border-radius:999px;overflow:hidden;display:inline-grid;place-items:center;background:var(--mb-color-primary-soft);color:var(--mb-color-primary);font-weight:800;border:1px solid var(--mb-color-border)}.mb-avatar img{inline-size:100%;block-size:100%;object-fit:cover}.mb-avatar--sm{inline-size:1.75rem;block-size:1.75rem;font-size:.75rem}.mb-avatar--lg{inline-size:3rem;block-size:3rem;font-size:1.1rem}\n"] }]
3361
+ }], propDecorators: { src: [{
3362
+ type: Input
3363
+ }], alt: [{
3364
+ type: Input
3365
+ }], name: [{
3366
+ type: Input
3367
+ }], size: [{
3368
+ type: Input
3369
+ }], hostClass: [{
3370
+ type: HostBinding,
3371
+ args: ['class']
3372
+ }] } });
3373
+
3374
+ class MbCardComponent {
3375
+ title = '';
3376
+ subtitle = '';
3377
+ padding = 'md';
3378
+ elevated = false;
3379
+ hostClass = 'mb-card-host';
3380
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3381
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbCardComponent, isStandalone: true, selector: "mb-card", inputs: { title: "title", subtitle: "subtitle", padding: "padding", elevated: "elevated" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<section class=\"mb-card\" [class.mb-card--elevated]=\"elevated\" [class]=\"'mb-card mb-card--padding-' + padding\">\n <header class=\"mb-card__header\" *ngIf=\"title || subtitle\">\n <h3 class=\"mb-card__title\" *ngIf=\"title\">{{ title }}</h3>\n <p class=\"mb-card__subtitle\" *ngIf=\"subtitle\">{{ subtitle }}</p>\n </header>\n <div class=\"mb-card__content\"><ng-content></ng-content></div>\n</section>\n", styles: [":host{display:block}.mb-card{border:1px solid var(--mb-color-border);border-radius:var(--mb-radius-lg);background:var(--mb-color-surface-elevated);color:var(--mb-color-text);overflow:hidden}.mb-card--elevated{box-shadow:var(--mb-shadow-md)}.mb-card__header{border-block-end:1px solid var(--mb-color-border);padding-block-end:.85rem;margin-block-end:.9rem}.mb-card__title{margin:0;font-size:1rem;font-weight:800}.mb-card__subtitle{margin:.25rem 0 0;color:var(--mb-color-muted);font-size:.86rem}.mb-card--padding-none{padding:0}.mb-card--padding-sm{padding:.75rem}.mb-card--padding-md{padding:1rem}.mb-card--padding-lg{padding:1.35rem}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3382
+ }
3383
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbCardComponent, decorators: [{
3384
+ type: Component,
3385
+ args: [{ selector: 'mb-card', standalone: true, imports: [NgIf], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section class=\"mb-card\" [class.mb-card--elevated]=\"elevated\" [class]=\"'mb-card mb-card--padding-' + padding\">\n <header class=\"mb-card__header\" *ngIf=\"title || subtitle\">\n <h3 class=\"mb-card__title\" *ngIf=\"title\">{{ title }}</h3>\n <p class=\"mb-card__subtitle\" *ngIf=\"subtitle\">{{ subtitle }}</p>\n </header>\n <div class=\"mb-card__content\"><ng-content></ng-content></div>\n</section>\n", styles: [":host{display:block}.mb-card{border:1px solid var(--mb-color-border);border-radius:var(--mb-radius-lg);background:var(--mb-color-surface-elevated);color:var(--mb-color-text);overflow:hidden}.mb-card--elevated{box-shadow:var(--mb-shadow-md)}.mb-card__header{border-block-end:1px solid var(--mb-color-border);padding-block-end:.85rem;margin-block-end:.9rem}.mb-card__title{margin:0;font-size:1rem;font-weight:800}.mb-card__subtitle{margin:.25rem 0 0;color:var(--mb-color-muted);font-size:.86rem}.mb-card--padding-none{padding:0}.mb-card--padding-sm{padding:.75rem}.mb-card--padding-md{padding:1rem}.mb-card--padding-lg{padding:1.35rem}\n"] }]
3386
+ }], propDecorators: { title: [{
3387
+ type: Input
3388
+ }], subtitle: [{
3389
+ type: Input
3390
+ }], padding: [{
3391
+ type: Input
3392
+ }], elevated: [{
3393
+ type: Input
3394
+ }], hostClass: [{
3395
+ type: HostBinding,
3396
+ args: ['class']
3397
+ }] } });
3398
+
3399
+ class MbAlertComponent {
3400
+ variant = 'info';
3401
+ title = '';
3402
+ closable = false;
3403
+ closed = new EventEmitter();
3404
+ hostClass = 'mb-alert-host';
3405
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbAlertComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3406
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbAlertComponent, isStandalone: true, selector: "mb-alert", inputs: { variant: "variant", title: "title", closable: "closable" }, outputs: { closed: "closed" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<div class=\"mb-alert\" [class]=\"'mb-alert mb-alert--' + variant\" role=\"alert\">\n <div class=\"mb-alert__body\">\n <strong *ngIf=\"title\" class=\"mb-alert__title\">{{ title }}</strong>\n <div class=\"mb-alert__content\"><ng-content></ng-content></div>\n </div>\n <button *ngIf=\"closable\" type=\"button\" class=\"mb-alert__close\" (click)=\"closed.emit()\" aria-label=\"Close\">\u00D7</button>\n</div>\n", styles: [":host{display:block}.mb-alert{display:flex;justify-content:space-between;gap:1rem;padding:.9rem 1rem;border-radius:var(--mb-radius-lg);border:1px solid transparent}.mb-alert__body{display:grid;gap:.25rem}.mb-alert__title{font-weight:850}.mb-alert__content{font-size:.9rem}.mb-alert__close{border:0;background:transparent;color:inherit;font-size:1.2rem;cursor:pointer}.mb-alert--info{background:var(--mb-color-primary-soft);color:var(--mb-color-primary)}.mb-alert--success{background:var(--mb-color-success-soft);color:var(--mb-color-success)}.mb-alert--warning{background:var(--mb-color-warning-soft);color:var(--mb-color-warning)}.mb-alert--danger{background:var(--mb-color-danger-soft);color:var(--mb-color-danger)}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3407
+ }
3408
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbAlertComponent, decorators: [{
3409
+ type: Component,
3410
+ args: [{ selector: 'mb-alert', standalone: true, imports: [NgIf], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mb-alert\" [class]=\"'mb-alert mb-alert--' + variant\" role=\"alert\">\n <div class=\"mb-alert__body\">\n <strong *ngIf=\"title\" class=\"mb-alert__title\">{{ title }}</strong>\n <div class=\"mb-alert__content\"><ng-content></ng-content></div>\n </div>\n <button *ngIf=\"closable\" type=\"button\" class=\"mb-alert__close\" (click)=\"closed.emit()\" aria-label=\"Close\">\u00D7</button>\n</div>\n", styles: [":host{display:block}.mb-alert{display:flex;justify-content:space-between;gap:1rem;padding:.9rem 1rem;border-radius:var(--mb-radius-lg);border:1px solid transparent}.mb-alert__body{display:grid;gap:.25rem}.mb-alert__title{font-weight:850}.mb-alert__content{font-size:.9rem}.mb-alert__close{border:0;background:transparent;color:inherit;font-size:1.2rem;cursor:pointer}.mb-alert--info{background:var(--mb-color-primary-soft);color:var(--mb-color-primary)}.mb-alert--success{background:var(--mb-color-success-soft);color:var(--mb-color-success)}.mb-alert--warning{background:var(--mb-color-warning-soft);color:var(--mb-color-warning)}.mb-alert--danger{background:var(--mb-color-danger-soft);color:var(--mb-color-danger)}\n"] }]
3411
+ }], propDecorators: { variant: [{
3412
+ type: Input
3413
+ }], title: [{
3414
+ type: Input
3415
+ }], closable: [{
3416
+ type: Input
3417
+ }], closed: [{
3418
+ type: Output
3419
+ }], hostClass: [{
3420
+ type: HostBinding,
3421
+ args: ['class']
3422
+ }] } });
3423
+
3424
+ class MbSpinnerComponent {
3425
+ size = 'md';
3426
+ label = 'Loading';
3427
+ hostClass = 'mb-spinner-host';
3428
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbSpinnerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3429
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbSpinnerComponent, isStandalone: true, selector: "mb-spinner", inputs: { size: "size", label: "label" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<span class=\"mb-spinner\" [class]=\"'mb-spinner mb-spinner--' + size\" role=\"status\" [attr.aria-label]=\"label\"></span>\n", styles: [":host{display:inline-flex}.mb-spinner{inline-size:1.35rem;block-size:1.35rem;border-radius:999px;border:2px solid var(--mb-color-border);border-block-start-color:var(--mb-color-primary);animation:mb-spin .75s linear infinite}.mb-spinner--sm{inline-size:1rem;block-size:1rem;border-width:2px}.mb-spinner--lg{inline-size:2rem;block-size:2rem;border-width:3px}@keyframes mb-spin{to{transform:rotate(360deg)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3430
+ }
3431
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbSpinnerComponent, decorators: [{
3432
+ type: Component,
3433
+ args: [{ selector: 'mb-spinner', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<span class=\"mb-spinner\" [class]=\"'mb-spinner mb-spinner--' + size\" role=\"status\" [attr.aria-label]=\"label\"></span>\n", styles: [":host{display:inline-flex}.mb-spinner{inline-size:1.35rem;block-size:1.35rem;border-radius:999px;border:2px solid var(--mb-color-border);border-block-start-color:var(--mb-color-primary);animation:mb-spin .75s linear infinite}.mb-spinner--sm{inline-size:1rem;block-size:1rem;border-width:2px}.mb-spinner--lg{inline-size:2rem;block-size:2rem;border-width:3px}@keyframes mb-spin{to{transform:rotate(360deg)}}\n"] }]
3434
+ }], propDecorators: { size: [{
3435
+ type: Input
3436
+ }], label: [{
3437
+ type: Input
3438
+ }], hostClass: [{
3439
+ type: HostBinding,
3440
+ args: ['class']
3441
+ }] } });
3442
+
3443
+ class MbSkeletonComponent {
3444
+ width = '100%';
3445
+ height = '1rem';
3446
+ radius = 'var(--mb-radius-md)';
3447
+ hostClass = 'mb-skeleton-host';
3448
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbSkeletonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3449
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbSkeletonComponent, isStandalone: true, selector: "mb-skeleton", inputs: { width: "width", height: "height", radius: "radius" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<span class=\"mb-skeleton\" [style.width]=\"width\" [style.height]=\"height\" [style.border-radius]=\"radius\"></span>\n", styles: [":host{display:block}.mb-skeleton{display:block;background:linear-gradient(90deg,var(--mb-color-surface),var(--mb-color-border),var(--mb-color-surface));background-size:200% 100%;animation:mb-skeleton 1.2s ease-in-out infinite}@keyframes mb-skeleton{0%{background-position:200% 0}to{background-position:-200% 0}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3450
+ }
3451
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbSkeletonComponent, decorators: [{
3452
+ type: Component,
3453
+ args: [{ selector: 'mb-skeleton', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<span class=\"mb-skeleton\" [style.width]=\"width\" [style.height]=\"height\" [style.border-radius]=\"radius\"></span>\n", styles: [":host{display:block}.mb-skeleton{display:block;background:linear-gradient(90deg,var(--mb-color-surface),var(--mb-color-border),var(--mb-color-surface));background-size:200% 100%;animation:mb-skeleton 1.2s ease-in-out infinite}@keyframes mb-skeleton{0%{background-position:200% 0}to{background-position:-200% 0}}\n"] }]
3454
+ }], propDecorators: { width: [{
3455
+ type: Input
3456
+ }], height: [{
3457
+ type: Input
3458
+ }], radius: [{
3459
+ type: Input
3460
+ }], hostClass: [{
3461
+ type: HostBinding,
3462
+ args: ['class']
3463
+ }] } });
3464
+
3465
+ class MbProgressComponent {
3466
+ value = 0;
3467
+ max = 100;
3468
+ label = false;
3469
+ indeterminate = false;
3470
+ hostClass = 'mb-progress-host';
3471
+ get percent() { return this.max <= 0 ? 0 : Math.max(0, Math.min(100, Math.round((this.value / this.max) * 100))); }
3472
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbProgressComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3473
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbProgressComponent, isStandalone: true, selector: "mb-progress", inputs: { value: "value", max: "max", label: "label", indeterminate: "indeterminate" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<div class=\"mb-progress\" [class.mb-progress--indeterminate]=\"indeterminate\" role=\"progressbar\" [attr.aria-valuenow]=\"indeterminate ? null : value\" [attr.aria-valuemax]=\"max\">\n <span class=\"mb-progress__bar\" [style.width.%]=\"indeterminate ? null : percent\"></span>\n</div>\n<span class=\"mb-progress__label\" *ngIf=\"label && !indeterminate\">{{ percent }}%</span>\n", styles: [":host{display:grid;gap:.35rem}.mb-progress{block-size:.55rem;border-radius:999px;background:var(--mb-color-surface);overflow:hidden;border:1px solid var(--mb-color-border)}.mb-progress__bar{display:block;block-size:100%;background:var(--mb-color-primary);border-radius:inherit;transition:width var(--mb-transition-normal)}.mb-progress--indeterminate .mb-progress__bar{inline-size:45%;animation:mb-progress-slide 1.2s ease-in-out infinite}.mb-progress__label{font-size:.78rem;color:var(--mb-color-muted)}@keyframes mb-progress-slide{0%{transform:translate(-120%)}to{transform:translate(250%)}}:host-context([dir=rtl]) .mb-progress--indeterminate .mb-progress__bar,:host-context(.mb-rtl) .mb-progress--indeterminate .mb-progress__bar{animation-name:mb-progress-slide-rtl}@keyframes mb-progress-slide-rtl{0%{transform:translate(120%)}to{transform:translate(-250%)}}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3474
+ }
3475
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbProgressComponent, decorators: [{
3476
+ type: Component,
3477
+ args: [{ selector: 'mb-progress', standalone: true, imports: [NgIf], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mb-progress\" [class.mb-progress--indeterminate]=\"indeterminate\" role=\"progressbar\" [attr.aria-valuenow]=\"indeterminate ? null : value\" [attr.aria-valuemax]=\"max\">\n <span class=\"mb-progress__bar\" [style.width.%]=\"indeterminate ? null : percent\"></span>\n</div>\n<span class=\"mb-progress__label\" *ngIf=\"label && !indeterminate\">{{ percent }}%</span>\n", styles: [":host{display:grid;gap:.35rem}.mb-progress{block-size:.55rem;border-radius:999px;background:var(--mb-color-surface);overflow:hidden;border:1px solid var(--mb-color-border)}.mb-progress__bar{display:block;block-size:100%;background:var(--mb-color-primary);border-radius:inherit;transition:width var(--mb-transition-normal)}.mb-progress--indeterminate .mb-progress__bar{inline-size:45%;animation:mb-progress-slide 1.2s ease-in-out infinite}.mb-progress__label{font-size:.78rem;color:var(--mb-color-muted)}@keyframes mb-progress-slide{0%{transform:translate(-120%)}to{transform:translate(250%)}}:host-context([dir=rtl]) .mb-progress--indeterminate .mb-progress__bar,:host-context(.mb-rtl) .mb-progress--indeterminate .mb-progress__bar{animation-name:mb-progress-slide-rtl}@keyframes mb-progress-slide-rtl{0%{transform:translate(120%)}to{transform:translate(-250%)}}\n"] }]
3478
+ }], propDecorators: { value: [{
3479
+ type: Input
3480
+ }], max: [{
3481
+ type: Input
3482
+ }], label: [{
3483
+ type: Input
3484
+ }], indeterminate: [{
3485
+ type: Input
3486
+ }], hostClass: [{
3487
+ type: HostBinding,
3488
+ args: ['class']
3489
+ }] } });
3490
+
3491
+ class MbEmptyComponent {
3492
+ messages = inject(MB_UI_MESSAGES);
3493
+ title;
3494
+ description = '';
3495
+ hostClass = 'mb-empty-host';
3496
+ get effectiveTitle() {
3497
+ return this.title ?? this.messages.empty.title;
3498
+ }
3499
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbEmptyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3500
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbEmptyComponent, isStandalone: true, selector: "mb-empty", inputs: { title: "title", description: "description" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<div class=\"mb-empty\">\n <div class=\"mb-empty__icon\" aria-hidden=\"true\">\u2205</div>\n <strong class=\"mb-empty__title\">{{ effectiveTitle }}</strong>\n <p class=\"mb-empty__desc\" *ngIf=\"description\">{{ description }}</p>\n <div class=\"mb-empty__actions\"><ng-content></ng-content></div>\n</div>\n", styles: [":host{display:block}.mb-empty{display:grid;justify-items:center;text-align:center;gap:.55rem;padding:2rem;color:var(--mb-color-muted)}.mb-empty__icon{inline-size:3rem;block-size:3rem;border-radius:999px;display:grid;place-items:center;background:var(--mb-color-surface);border:1px solid var(--mb-color-border);font-size:1.6rem}.mb-empty__title{color:var(--mb-color-text);font-size:.95rem}.mb-empty__desc{margin:0;font-size:.86rem}.mb-empty__actions{margin-block-start:.5rem}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3501
+ }
3502
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbEmptyComponent, decorators: [{
3503
+ type: Component,
3504
+ args: [{ selector: 'mb-empty', standalone: true, imports: [NgIf], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mb-empty\">\n <div class=\"mb-empty__icon\" aria-hidden=\"true\">\u2205</div>\n <strong class=\"mb-empty__title\">{{ effectiveTitle }}</strong>\n <p class=\"mb-empty__desc\" *ngIf=\"description\">{{ description }}</p>\n <div class=\"mb-empty__actions\"><ng-content></ng-content></div>\n</div>\n", styles: [":host{display:block}.mb-empty{display:grid;justify-items:center;text-align:center;gap:.55rem;padding:2rem;color:var(--mb-color-muted)}.mb-empty__icon{inline-size:3rem;block-size:3rem;border-radius:999px;display:grid;place-items:center;background:var(--mb-color-surface);border:1px solid var(--mb-color-border);font-size:1.6rem}.mb-empty__title{color:var(--mb-color-text);font-size:.95rem}.mb-empty__desc{margin:0;font-size:.86rem}.mb-empty__actions{margin-block-start:.5rem}\n"] }]
3505
+ }], propDecorators: { title: [{
3506
+ type: Input
3507
+ }], description: [{
3508
+ type: Input
3509
+ }], hostClass: [{
3510
+ type: HostBinding,
3511
+ args: ['class']
3512
+ }] } });
3513
+
3514
+ class MbDividerComponent {
3515
+ orientation = 'horizontal';
3516
+ get hostClass() { return `mb-divider-host mb-divider-host--${this.orientation}`; }
3517
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbDividerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3518
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbDividerComponent, isStandalone: true, selector: "mb-divider", inputs: { orientation: "orientation" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: '<span class="mb-divider__text"><ng-content></ng-content></span>', isInline: true, styles: [":host{display:flex;align-items:center;color:var(--mb-color-muted)}:host(.mb-divider-host--horizontal){inline-size:100%;border-block-start:1px solid var(--mb-color-border);margin-block:1rem;justify-content:center}:host(.mb-divider-host--vertical){align-self:stretch;border-inline-start:1px solid var(--mb-color-border);margin-inline:.75rem}.mb-divider__text{background:var(--mb-color-bg);padding-inline:.5rem;margin-block-start:-.75rem;font-size:.78rem}:host(.mb-divider-host--vertical) .mb-divider__text{display:none}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3519
+ }
3520
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbDividerComponent, decorators: [{
3521
+ type: Component,
3522
+ args: [{ selector: 'mb-divider', standalone: true, template: '<span class="mb-divider__text"><ng-content></ng-content></span>', changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:flex;align-items:center;color:var(--mb-color-muted)}:host(.mb-divider-host--horizontal){inline-size:100%;border-block-start:1px solid var(--mb-color-border);margin-block:1rem;justify-content:center}:host(.mb-divider-host--vertical){align-self:stretch;border-inline-start:1px solid var(--mb-color-border);margin-inline:.75rem}.mb-divider__text{background:var(--mb-color-bg);padding-inline:.5rem;margin-block-start:-.75rem;font-size:.78rem}:host(.mb-divider-host--vertical) .mb-divider__text{display:none}\n"] }]
3523
+ }], propDecorators: { orientation: [{
3524
+ type: Input
3525
+ }], hostClass: [{
3526
+ type: HostBinding,
3527
+ args: ['class']
3528
+ }] } });
3529
+
3530
+ class MbBreadcrumbComponent {
3531
+ items = [];
3532
+ itemClick = new EventEmitter();
3533
+ hostClass = 'mb-breadcrumb-host';
3534
+ trackByIndex(index) { return index; }
3535
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbBreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3536
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbBreadcrumbComponent, isStandalone: true, selector: "mb-breadcrumb", inputs: { items: "items" }, outputs: { itemClick: "itemClick" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<nav class=\"mb-breadcrumb\" aria-label=\"Breadcrumb\">\n <ol>\n <li *ngFor=\"let item of items; let last = last; trackBy: trackByIndex\">\n <button type=\"button\" [disabled]=\"item.disabled || last\" (click)=\"itemClick.emit(item)\">{{ item.label }}</button>\n <span *ngIf=\"!last\" aria-hidden=\"true\">/</span>\n </li>\n </ol>\n</nav>\n", styles: [".mb-breadcrumb ol{list-style:none;padding:0;margin:0;display:flex;flex-wrap:wrap;gap:.35rem;align-items:center}.mb-breadcrumb li{display:inline-flex;align-items:center;gap:.35rem;color:var(--mb-color-muted)}.mb-breadcrumb button{border:0;background:transparent;padding:.15rem .2rem;color:var(--mb-color-primary);cursor:pointer;font:inherit}.mb-breadcrumb button:disabled{color:var(--mb-color-muted);cursor:default}\n"], dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3537
+ }
3538
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbBreadcrumbComponent, decorators: [{
3539
+ type: Component,
3540
+ args: [{ selector: 'mb-breadcrumb', standalone: true, imports: [NgFor, NgIf], changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav class=\"mb-breadcrumb\" aria-label=\"Breadcrumb\">\n <ol>\n <li *ngFor=\"let item of items; let last = last; trackBy: trackByIndex\">\n <button type=\"button\" [disabled]=\"item.disabled || last\" (click)=\"itemClick.emit(item)\">{{ item.label }}</button>\n <span *ngIf=\"!last\" aria-hidden=\"true\">/</span>\n </li>\n </ol>\n</nav>\n", styles: [".mb-breadcrumb ol{list-style:none;padding:0;margin:0;display:flex;flex-wrap:wrap;gap:.35rem;align-items:center}.mb-breadcrumb li{display:inline-flex;align-items:center;gap:.35rem;color:var(--mb-color-muted)}.mb-breadcrumb button{border:0;background:transparent;padding:.15rem .2rem;color:var(--mb-color-primary);cursor:pointer;font:inherit}.mb-breadcrumb button:disabled{color:var(--mb-color-muted);cursor:default}\n"] }]
3541
+ }], propDecorators: { items: [{
3542
+ type: Input
3543
+ }], itemClick: [{
3544
+ type: Output
3545
+ }], hostClass: [{
3546
+ type: HostBinding,
3547
+ args: ['class']
3548
+ }] } });
3549
+
3550
+ class MbTabsComponent {
3551
+ tabs = [];
3552
+ activeId = '';
3553
+ variant = 'line';
3554
+ activeIdChange = new EventEmitter();
3555
+ hostClass = 'mb-tabs-host';
3556
+ select(tab) { if (!tab.disabled)
3557
+ this.activeIdChange.emit(tab.id); }
3558
+ trackById(_, item) { return item.id; }
3559
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3560
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbTabsComponent, isStandalone: true, selector: "mb-tabs", inputs: { tabs: "tabs", activeId: "activeId", variant: "variant" }, outputs: { activeIdChange: "activeIdChange" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<div class=\"mb-tabs\" [class.mb-tabs--pills]=\"variant === 'pills'\" role=\"tablist\">\n <button\n *ngFor=\"let tab of tabs; trackBy: trackById\"\n type=\"button\"\n class=\"mb-tabs__tab\"\n role=\"tab\"\n [class.mb-tabs__tab--active]=\"tab.id === activeId\"\n [disabled]=\"tab.disabled\"\n [attr.aria-selected]=\"tab.id === activeId\"\n (click)=\"select(tab)\">\n <span>{{ tab.label }}</span>\n <span *ngIf=\"tab.badge !== undefined\" class=\"mb-tabs__badge\">{{ tab.badge }}</span>\n </button>\n</div>\n", styles: [".mb-tabs{display:flex;gap:.25rem;border-block-end:1px solid var(--mb-color-border);overflow-x:auto}.mb-tabs__tab{border:0;background:transparent;color:var(--mb-color-muted);font:inherit;cursor:pointer;padding:.75rem 1rem;border-block-end:2px solid transparent;display:inline-flex;gap:.45rem;align-items:center;font-weight:700;white-space:nowrap}.mb-tabs__tab--active{color:var(--mb-color-primary);border-block-end-color:var(--mb-color-primary)}.mb-tabs__tab:disabled{opacity:.55;cursor:not-allowed}.mb-tabs__badge{background:var(--mb-color-surface);border:1px solid var(--mb-color-border);border-radius:999px;padding:.05rem .38rem;font-size:.72rem}.mb-tabs--pills{border:0;gap:.4rem}.mb-tabs--pills .mb-tabs__tab{border:1px solid var(--mb-color-border);border-radius:999px;padding:.5rem .85rem}.mb-tabs--pills .mb-tabs__tab--active{background:var(--mb-color-primary);color:#fff;border-color:var(--mb-color-primary)}\n"], dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3561
+ }
3562
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbTabsComponent, decorators: [{
3563
+ type: Component,
3564
+ args: [{ selector: 'mb-tabs', standalone: true, imports: [NgFor, NgIf], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mb-tabs\" [class.mb-tabs--pills]=\"variant === 'pills'\" role=\"tablist\">\n <button\n *ngFor=\"let tab of tabs; trackBy: trackById\"\n type=\"button\"\n class=\"mb-tabs__tab\"\n role=\"tab\"\n [class.mb-tabs__tab--active]=\"tab.id === activeId\"\n [disabled]=\"tab.disabled\"\n [attr.aria-selected]=\"tab.id === activeId\"\n (click)=\"select(tab)\">\n <span>{{ tab.label }}</span>\n <span *ngIf=\"tab.badge !== undefined\" class=\"mb-tabs__badge\">{{ tab.badge }}</span>\n </button>\n</div>\n", styles: [".mb-tabs{display:flex;gap:.25rem;border-block-end:1px solid var(--mb-color-border);overflow-x:auto}.mb-tabs__tab{border:0;background:transparent;color:var(--mb-color-muted);font:inherit;cursor:pointer;padding:.75rem 1rem;border-block-end:2px solid transparent;display:inline-flex;gap:.45rem;align-items:center;font-weight:700;white-space:nowrap}.mb-tabs__tab--active{color:var(--mb-color-primary);border-block-end-color:var(--mb-color-primary)}.mb-tabs__tab:disabled{opacity:.55;cursor:not-allowed}.mb-tabs__badge{background:var(--mb-color-surface);border:1px solid var(--mb-color-border);border-radius:999px;padding:.05rem .38rem;font-size:.72rem}.mb-tabs--pills{border:0;gap:.4rem}.mb-tabs--pills .mb-tabs__tab{border:1px solid var(--mb-color-border);border-radius:999px;padding:.5rem .85rem}.mb-tabs--pills .mb-tabs__tab--active{background:var(--mb-color-primary);color:#fff;border-color:var(--mb-color-primary)}\n"] }]
3565
+ }], propDecorators: { tabs: [{
3566
+ type: Input
3567
+ }], activeId: [{
3568
+ type: Input
3569
+ }], variant: [{
3570
+ type: Input
3571
+ }], activeIdChange: [{
3572
+ type: Output
3573
+ }], hostClass: [{
3574
+ type: HostBinding,
3575
+ args: ['class']
3576
+ }] } });
3577
+
3578
+ class MbAccordionComponent {
3579
+ items = [];
3580
+ multiple = false;
3581
+ itemsChange = new EventEmitter();
3582
+ hostClass = 'mb-accordion-host';
3583
+ toggle(item) {
3584
+ if (item.disabled)
3585
+ return;
3586
+ const next = this.items.map((candidate) => {
3587
+ if (candidate.id === item.id)
3588
+ return { ...candidate, open: !candidate.open };
3589
+ return this.multiple ? candidate : { ...candidate, open: false };
3590
+ });
3591
+ this.items = next;
3592
+ this.itemsChange.emit(next);
3593
+ }
3594
+ trackById(_, item) { return item.id; }
3595
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbAccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3596
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbAccordionComponent, isStandalone: true, selector: "mb-accordion", inputs: { items: "items", multiple: "multiple" }, outputs: { itemsChange: "itemsChange" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<div class=\"mb-accordion\">\n <section *ngFor=\"let item of items; trackBy: trackById\" class=\"mb-accordion__item\" [class.mb-accordion__item--open]=\"item.open\">\n <button type=\"button\" class=\"mb-accordion__header\" [disabled]=\"item.disabled\" (click)=\"toggle(item)\">\n <span>{{ item.title }}</span>\n <span aria-hidden=\"true\">\u2304</span>\n </button>\n <div class=\"mb-accordion__panel\" *ngIf=\"item.open\">\n <ng-container *ngIf=\"item.content; else projected\">{{ item.content }}</ng-container>\n <ng-template #projected><ng-content></ng-content></ng-template>\n </div>\n </section>\n</div>\n", styles: [".mb-accordion{border:1px solid var(--mb-color-border);border-radius:var(--mb-radius-lg);overflow:hidden;background:var(--mb-color-bg)}.mb-accordion__item+.mb-accordion__item{border-block-start:1px solid var(--mb-color-border)}.mb-accordion__header{inline-size:100%;display:flex;justify-content:space-between;align-items:center;gap:1rem;padding:.85rem 1rem;border:0;background:transparent;color:var(--mb-color-text);cursor:pointer;font:inherit;font-weight:800;text-align:start}.mb-accordion__header:disabled{opacity:.55;cursor:not-allowed}.mb-accordion__item--open .mb-accordion__header span:last-child{transform:rotate(180deg)}.mb-accordion__panel{padding:0 1rem 1rem;color:var(--mb-color-muted);line-height:1.7}\n"], dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3597
+ }
3598
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbAccordionComponent, decorators: [{
3599
+ type: Component,
3600
+ args: [{ selector: 'mb-accordion', standalone: true, imports: [NgFor, NgIf], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mb-accordion\">\n <section *ngFor=\"let item of items; trackBy: trackById\" class=\"mb-accordion__item\" [class.mb-accordion__item--open]=\"item.open\">\n <button type=\"button\" class=\"mb-accordion__header\" [disabled]=\"item.disabled\" (click)=\"toggle(item)\">\n <span>{{ item.title }}</span>\n <span aria-hidden=\"true\">\u2304</span>\n </button>\n <div class=\"mb-accordion__panel\" *ngIf=\"item.open\">\n <ng-container *ngIf=\"item.content; else projected\">{{ item.content }}</ng-container>\n <ng-template #projected><ng-content></ng-content></ng-template>\n </div>\n </section>\n</div>\n", styles: [".mb-accordion{border:1px solid var(--mb-color-border);border-radius:var(--mb-radius-lg);overflow:hidden;background:var(--mb-color-bg)}.mb-accordion__item+.mb-accordion__item{border-block-start:1px solid var(--mb-color-border)}.mb-accordion__header{inline-size:100%;display:flex;justify-content:space-between;align-items:center;gap:1rem;padding:.85rem 1rem;border:0;background:transparent;color:var(--mb-color-text);cursor:pointer;font:inherit;font-weight:800;text-align:start}.mb-accordion__header:disabled{opacity:.55;cursor:not-allowed}.mb-accordion__item--open .mb-accordion__header span:last-child{transform:rotate(180deg)}.mb-accordion__panel{padding:0 1rem 1rem;color:var(--mb-color-muted);line-height:1.7}\n"] }]
3601
+ }], propDecorators: { items: [{
3602
+ type: Input
3603
+ }], multiple: [{
3604
+ type: Input
3605
+ }], itemsChange: [{
3606
+ type: Output
3607
+ }], hostClass: [{
3608
+ type: HostBinding,
3609
+ args: ['class']
3610
+ }] } });
3611
+
3612
+ class MbPaginationComponent {
3613
+ messages = inject(MB_UI_MESSAGES);
3614
+ page = 1;
3615
+ pageSize = 10;
3616
+ total = 0;
3617
+ maxButtons = 5;
3618
+ pageChange = new EventEmitter();
3619
+ hostClass = 'mb-pagination-host';
3620
+ get pageCount() { return Math.max(1, Math.ceil(this.total / Math.max(1, this.pageSize))); }
3621
+ get pages() {
3622
+ const count = this.pageCount;
3623
+ const half = Math.floor(this.maxButtons / 2);
3624
+ let start = Math.max(1, this.page - half);
3625
+ const end = Math.min(count, start + this.maxButtons - 1);
3626
+ start = Math.max(1, end - this.maxButtons + 1);
3627
+ return Array.from({ length: end - start + 1 }, (_, i) => start + i);
3628
+ }
3629
+ go(page) {
3630
+ const next = Math.max(1, Math.min(this.pageCount, page));
3631
+ if (next !== this.page)
3632
+ this.pageChange.emit(next);
3633
+ }
3634
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbPaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3635
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbPaginationComponent, isStandalone: true, selector: "mb-pagination", inputs: { page: "page", pageSize: "pageSize", total: "total", maxButtons: "maxButtons" }, outputs: { pageChange: "pageChange" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<nav class=\"mb-pagination\" [attr.aria-label]=\"messages.pagination.ariaLabel\">\n <button type=\"button\" (click)=\"go(page - 1)\" [disabled]=\"page <= 1\">\u2039</button>\n <button *ngFor=\"let p of pages\" type=\"button\" [class.mb-pagination__page--active]=\"p === page\" (click)=\"go(p)\">{{ p }}</button>\n <button type=\"button\" (click)=\"go(page + 1)\" [disabled]=\"page >= pageCount\">\u203A</button>\n <span class=\"mb-pagination__meta\" *ngIf=\"total\">{{ page }} / {{ pageCount }}</span>\n</nav>\n", styles: [".mb-pagination{display:flex;align-items:center;gap:.35rem;flex-wrap:wrap}.mb-pagination button{min-inline-size:2.2rem;block-size:2.2rem;border:1px solid var(--mb-color-border);background:var(--mb-color-bg);color:var(--mb-color-text);border-radius:var(--mb-radius-md);cursor:pointer;font:inherit;font-weight:700}.mb-pagination button:hover:not(:disabled){border-color:var(--mb-color-primary);color:var(--mb-color-primary)}.mb-pagination button:disabled{opacity:.45;cursor:not-allowed}.mb-pagination__page--active{background:var(--mb-color-primary)!important;border-color:var(--mb-color-primary)!important;color:#fff!important}.mb-pagination__meta{color:var(--mb-color-muted);font-size:.82rem;margin-inline-start:.35rem}\n"], dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3636
+ }
3637
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbPaginationComponent, decorators: [{
3638
+ type: Component,
3639
+ args: [{ selector: 'mb-pagination', standalone: true, imports: [NgFor, NgIf], changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav class=\"mb-pagination\" [attr.aria-label]=\"messages.pagination.ariaLabel\">\n <button type=\"button\" (click)=\"go(page - 1)\" [disabled]=\"page <= 1\">\u2039</button>\n <button *ngFor=\"let p of pages\" type=\"button\" [class.mb-pagination__page--active]=\"p === page\" (click)=\"go(p)\">{{ p }}</button>\n <button type=\"button\" (click)=\"go(page + 1)\" [disabled]=\"page >= pageCount\">\u203A</button>\n <span class=\"mb-pagination__meta\" *ngIf=\"total\">{{ page }} / {{ pageCount }}</span>\n</nav>\n", styles: [".mb-pagination{display:flex;align-items:center;gap:.35rem;flex-wrap:wrap}.mb-pagination button{min-inline-size:2.2rem;block-size:2.2rem;border:1px solid var(--mb-color-border);background:var(--mb-color-bg);color:var(--mb-color-text);border-radius:var(--mb-radius-md);cursor:pointer;font:inherit;font-weight:700}.mb-pagination button:hover:not(:disabled){border-color:var(--mb-color-primary);color:var(--mb-color-primary)}.mb-pagination button:disabled{opacity:.45;cursor:not-allowed}.mb-pagination__page--active{background:var(--mb-color-primary)!important;border-color:var(--mb-color-primary)!important;color:#fff!important}.mb-pagination__meta{color:var(--mb-color-muted);font-size:.82rem;margin-inline-start:.35rem}\n"] }]
3640
+ }], propDecorators: { page: [{
3641
+ type: Input
3642
+ }], pageSize: [{
3643
+ type: Input
3644
+ }], total: [{
3645
+ type: Input
3646
+ }], maxButtons: [{
3647
+ type: Input
3648
+ }], pageChange: [{
3649
+ type: Output
3650
+ }], hostClass: [{
3651
+ type: HostBinding,
3652
+ args: ['class']
3653
+ }] } });
3654
+
3655
+ class MbDrawerComponent {
3656
+ open = false;
3657
+ title = '';
3658
+ position = 'end';
3659
+ closeOnBackdrop = true;
3660
+ openChange = new EventEmitter();
3661
+ hostClass = 'mb-drawer-host';
3662
+ close() { this.openChange.emit(false); }
3663
+ backdrop() { if (this.closeOnBackdrop)
3664
+ this.close(); }
3665
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3666
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbDrawerComponent, isStandalone: true, selector: "mb-drawer", inputs: { open: "open", title: "title", position: "position", closeOnBackdrop: "closeOnBackdrop" }, outputs: { openChange: "openChange" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<div class=\"mb-drawer-backdrop\" *ngIf=\"open\" (click)=\"backdrop()\"></div>\n<aside *ngIf=\"open\" class=\"mb-drawer\" [class]=\"'mb-drawer mb-drawer--' + position\" role=\"dialog\" aria-modal=\"true\">\n <header class=\"mb-drawer__header\" *ngIf=\"title\">\n <strong>{{ title }}</strong>\n <button type=\"button\" (click)=\"close()\" aria-label=\"Close\">\u00D7</button>\n </header>\n <div class=\"mb-drawer__content\"><ng-content></ng-content></div>\n</aside>\n", styles: [".mb-drawer-backdrop{position:fixed;inset:0;background:#0f172a73;z-index:1000}.mb-drawer{position:fixed;z-index:1001;background:var(--mb-color-bg);color:var(--mb-color-text);box-shadow:var(--mb-shadow-lg);display:grid;grid-template-rows:auto 1fr}.mb-drawer--end{inset-block:0;inset-inline-end:0;inline-size:min(28rem,92vw)}.mb-drawer--start{inset-block:0;inset-inline-start:0;inline-size:min(28rem,92vw)}.mb-drawer--top{inset-inline:0;inset-block-start:0;block-size:min(24rem,72vh)}.mb-drawer--bottom{inset-inline:0;inset-block-end:0;block-size:min(24rem,72vh)}.mb-drawer__header{padding:1rem;border-block-end:1px solid var(--mb-color-border);display:flex;justify-content:space-between;gap:1rem;align-items:center}.mb-drawer__header button{border:0;background:transparent;color:inherit;font-size:1.3rem;cursor:pointer}.mb-drawer__content{padding:1rem;overflow:auto}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3667
+ }
3668
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbDrawerComponent, decorators: [{
3669
+ type: Component,
3670
+ args: [{ selector: 'mb-drawer', standalone: true, imports: [NgIf], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mb-drawer-backdrop\" *ngIf=\"open\" (click)=\"backdrop()\"></div>\n<aside *ngIf=\"open\" class=\"mb-drawer\" [class]=\"'mb-drawer mb-drawer--' + position\" role=\"dialog\" aria-modal=\"true\">\n <header class=\"mb-drawer__header\" *ngIf=\"title\">\n <strong>{{ title }}</strong>\n <button type=\"button\" (click)=\"close()\" aria-label=\"Close\">\u00D7</button>\n </header>\n <div class=\"mb-drawer__content\"><ng-content></ng-content></div>\n</aside>\n", styles: [".mb-drawer-backdrop{position:fixed;inset:0;background:#0f172a73;z-index:1000}.mb-drawer{position:fixed;z-index:1001;background:var(--mb-color-bg);color:var(--mb-color-text);box-shadow:var(--mb-shadow-lg);display:grid;grid-template-rows:auto 1fr}.mb-drawer--end{inset-block:0;inset-inline-end:0;inline-size:min(28rem,92vw)}.mb-drawer--start{inset-block:0;inset-inline-start:0;inline-size:min(28rem,92vw)}.mb-drawer--top{inset-inline:0;inset-block-start:0;block-size:min(24rem,72vh)}.mb-drawer--bottom{inset-inline:0;inset-block-end:0;block-size:min(24rem,72vh)}.mb-drawer__header{padding:1rem;border-block-end:1px solid var(--mb-color-border);display:flex;justify-content:space-between;gap:1rem;align-items:center}.mb-drawer__header button{border:0;background:transparent;color:inherit;font-size:1.3rem;cursor:pointer}.mb-drawer__content{padding:1rem;overflow:auto}\n"] }]
3671
+ }], propDecorators: { open: [{
3672
+ type: Input
3673
+ }], title: [{
3674
+ type: Input
3675
+ }], position: [{
3676
+ type: Input
3677
+ }], closeOnBackdrop: [{
3678
+ type: Input
3679
+ }], openChange: [{
3680
+ type: Output
3681
+ }], hostClass: [{
3682
+ type: HostBinding,
3683
+ args: ['class']
3684
+ }] } });
3685
+
3686
+ class MbDropdownComponent {
3687
+ open = false;
3688
+ align = 'start';
3689
+ hostClass = 'mb-dropdown-host';
3690
+ toggle() { this.open = !this.open; }
3691
+ close() { this.open = false; }
3692
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3693
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbDropdownComponent, isStandalone: true, selector: "mb-dropdown", inputs: { open: "open", align: "align" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<div class=\"mb-dropdown\" [class.mb-dropdown--open]=\"open\" [class.mb-dropdown--end]=\"align === 'end'\">\n <div class=\"mb-dropdown__trigger\" (click)=\"toggle()\"><ng-content select=\"[mbDropdownTrigger]\"></ng-content></div>\n <div class=\"mb-dropdown__panel\" *ngIf=\"open\"><ng-content></ng-content></div>\n</div>\n", styles: [":host{display:inline-block;position:relative}.mb-dropdown{position:relative}.mb-dropdown__trigger{display:inline-flex}.mb-dropdown__panel{position:absolute;z-index:50;inset-block-start:calc(100% + .35rem);inset-inline-start:0;min-inline-size:12rem;padding:.45rem;border:1px solid var(--mb-color-border);border-radius:var(--mb-radius-lg);background:var(--mb-color-surface-elevated);box-shadow:var(--mb-shadow-md)}.mb-dropdown--end .mb-dropdown__panel{inset-inline-start:auto;inset-inline-end:0}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3694
+ }
3695
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbDropdownComponent, decorators: [{
3696
+ type: Component,
3697
+ args: [{ selector: 'mb-dropdown', standalone: true, imports: [NgIf], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mb-dropdown\" [class.mb-dropdown--open]=\"open\" [class.mb-dropdown--end]=\"align === 'end'\">\n <div class=\"mb-dropdown__trigger\" (click)=\"toggle()\"><ng-content select=\"[mbDropdownTrigger]\"></ng-content></div>\n <div class=\"mb-dropdown__panel\" *ngIf=\"open\"><ng-content></ng-content></div>\n</div>\n", styles: [":host{display:inline-block;position:relative}.mb-dropdown{position:relative}.mb-dropdown__trigger{display:inline-flex}.mb-dropdown__panel{position:absolute;z-index:50;inset-block-start:calc(100% + .35rem);inset-inline-start:0;min-inline-size:12rem;padding:.45rem;border:1px solid var(--mb-color-border);border-radius:var(--mb-radius-lg);background:var(--mb-color-surface-elevated);box-shadow:var(--mb-shadow-md)}.mb-dropdown--end .mb-dropdown__panel{inset-inline-start:auto;inset-inline-end:0}\n"] }]
3698
+ }], propDecorators: { open: [{
3699
+ type: Input
3700
+ }], align: [{
3701
+ type: Input
3702
+ }], hostClass: [{
3703
+ type: HostBinding,
3704
+ args: ['class']
3705
+ }] } });
3706
+
3707
+ class MbTooltipDirective {
3708
+ elementRef;
3709
+ renderer;
3710
+ text = '';
3711
+ tooltip;
3712
+ constructor(elementRef, renderer) {
3713
+ this.elementRef = elementRef;
3714
+ this.renderer = renderer;
3715
+ }
3716
+ show() {
3717
+ if (!this.text || this.tooltip)
3718
+ return;
3719
+ const rect = this.elementRef.nativeElement.getBoundingClientRect();
3720
+ const el = this.renderer.createElement('div');
3721
+ this.renderer.addClass(el, 'mb-tooltip-panel');
3722
+ this.renderer.setProperty(el, 'textContent', this.text);
3723
+ this.renderer.appendChild(document.body, el);
3724
+ const top = rect.top + window.scrollY - el.offsetHeight - 8;
3725
+ const left = rect.left + window.scrollX + rect.width / 2 - el.offsetWidth / 2;
3726
+ this.renderer.setStyle(el, 'top', `${Math.max(8, top)}px`);
3727
+ this.renderer.setStyle(el, 'left', `${Math.max(8, left)}px`);
3728
+ this.tooltip = el;
3729
+ }
3730
+ hide() {
3731
+ if (this.tooltip) {
3732
+ this.renderer.removeChild(document.body, this.tooltip);
3733
+ this.tooltip = undefined;
3734
+ }
3735
+ }
3736
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbTooltipDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
3737
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: MbTooltipDirective, isStandalone: true, selector: "[mbTooltip]", inputs: { text: ["mbTooltip", "text"] }, host: { listeners: { "mouseenter": "show()", "mouseleave": "hide()" } }, ngImport: i0 });
3738
+ }
3739
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbTooltipDirective, decorators: [{
3740
+ type: Directive,
3741
+ args: [{ selector: '[mbTooltip]', standalone: true }]
3742
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { text: [{
3743
+ type: Input,
3744
+ args: ['mbTooltip']
3745
+ }], show: [{
3746
+ type: HostListener,
3747
+ args: ['mouseenter']
3748
+ }], hide: [{
3749
+ type: HostListener,
3750
+ args: ['mouseleave']
3751
+ }] } });
3752
+
3753
+ class MbConfirmDialogComponent {
3754
+ options;
3755
+ dialogRef;
3756
+ messages = inject(MB_UI_MESSAGES);
3757
+ constructor(options, dialogRef) {
3758
+ this.options = options;
3759
+ this.dialogRef = dialogRef;
3760
+ }
3761
+ get tone() {
3762
+ return this.options.tone ?? 'default';
3763
+ }
3764
+ get titleText() {
3765
+ return this.options.title ?? this.messages.confirm.title;
3766
+ }
3767
+ get okText() {
3768
+ return this.options.okText ?? this.messages.confirm.okText;
3769
+ }
3770
+ get cancelText() {
3771
+ return this.options.cancelText ?? this.messages.confirm.cancelText;
3772
+ }
3773
+ cancel() {
3774
+ this.dialogRef.close(false);
3775
+ }
3776
+ accept() {
3777
+ this.dialogRef.close(true);
3778
+ }
3779
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbConfirmDialogComponent, deps: [{ token: MB_DIALOG_DATA }, { token: MbDialogRef }], target: i0.ɵɵFactoryTarget.Component });
3780
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MbConfirmDialogComponent, isStandalone: true, selector: "mb-confirm-dialog", ngImport: i0, template: "<section class=\"mb-confirm\" [ngClass]=\"'mb-confirm--' + tone\">\n <div class=\"mb-confirm__icon\" aria-hidden=\"true\">\n <span>{{ tone === 'danger' ? '!' : tone === 'warning' ? '?' : '\u2713' }}</span>\n </div>\n <div class=\"mb-confirm__content\">\n <h2 class=\"mb-confirm__title\">{{ titleText }}</h2>\n <p class=\"mb-confirm__message\">{{ options.message }}</p>\n </div>\n <div class=\"mb-confirm__actions\">\n <mb-button variant=\"ghost\" (clicked)=\"cancel()\">{{ cancelText }}</mb-button>\n <mb-button [variant]=\"tone === 'danger' ? 'danger' : 'primary'\" (clicked)=\"accept()\">{{ okText }}</mb-button>\n </div>\n</section>\n", styles: [".mb-confirm{display:grid;grid-template-columns:auto 1fr;gap:1rem;padding:1.25rem;min-inline-size:min(30rem,100vw - 2rem)}.mb-confirm__icon{inline-size:2.75rem;block-size:2.75rem;border-radius:999px;display:grid;place-items:center;background:color-mix(in srgb,var(--mb-color-primary, #2563eb) 14%,transparent);color:var(--mb-color-primary, #2563eb);font-weight:800}.mb-confirm--danger .mb-confirm__icon{background:color-mix(in srgb,var(--mb-color-danger, #dc2626) 14%,transparent);color:var(--mb-color-danger, #dc2626)}.mb-confirm--warning .mb-confirm__icon{background:color-mix(in srgb,var(--mb-color-warning, #f59e0b) 16%,transparent);color:var(--mb-color-warning, #f59e0b)}.mb-confirm__title{margin:0;color:var(--mb-color-text, #0f172a);font-size:1.05rem}.mb-confirm__message{margin:.4rem 0 0;color:var(--mb-color-muted, #64748b);line-height:1.8}.mb-confirm__actions{grid-column:1/-1;display:flex;justify-content:flex-end;gap:.5rem;margin-block-start:.5rem}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MbButtonComponent, selector: "mb-button", inputs: ["variant", "size", "type", "disabled", "loading", "fullWidth", "ariaLabel"], outputs: ["clicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3781
+ }
3782
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbConfirmDialogComponent, decorators: [{
3783
+ type: Component,
3784
+ args: [{ selector: 'mb-confirm-dialog', standalone: true, imports: [NgClass, MbButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section class=\"mb-confirm\" [ngClass]=\"'mb-confirm--' + tone\">\n <div class=\"mb-confirm__icon\" aria-hidden=\"true\">\n <span>{{ tone === 'danger' ? '!' : tone === 'warning' ? '?' : '\u2713' }}</span>\n </div>\n <div class=\"mb-confirm__content\">\n <h2 class=\"mb-confirm__title\">{{ titleText }}</h2>\n <p class=\"mb-confirm__message\">{{ options.message }}</p>\n </div>\n <div class=\"mb-confirm__actions\">\n <mb-button variant=\"ghost\" (clicked)=\"cancel()\">{{ cancelText }}</mb-button>\n <mb-button [variant]=\"tone === 'danger' ? 'danger' : 'primary'\" (clicked)=\"accept()\">{{ okText }}</mb-button>\n </div>\n</section>\n", styles: [".mb-confirm{display:grid;grid-template-columns:auto 1fr;gap:1rem;padding:1.25rem;min-inline-size:min(30rem,100vw - 2rem)}.mb-confirm__icon{inline-size:2.75rem;block-size:2.75rem;border-radius:999px;display:grid;place-items:center;background:color-mix(in srgb,var(--mb-color-primary, #2563eb) 14%,transparent);color:var(--mb-color-primary, #2563eb);font-weight:800}.mb-confirm--danger .mb-confirm__icon{background:color-mix(in srgb,var(--mb-color-danger, #dc2626) 14%,transparent);color:var(--mb-color-danger, #dc2626)}.mb-confirm--warning .mb-confirm__icon{background:color-mix(in srgb,var(--mb-color-warning, #f59e0b) 16%,transparent);color:var(--mb-color-warning, #f59e0b)}.mb-confirm__title{margin:0;color:var(--mb-color-text, #0f172a);font-size:1.05rem}.mb-confirm__message{margin:.4rem 0 0;color:var(--mb-color-muted, #64748b);line-height:1.8}.mb-confirm__actions{grid-column:1/-1;display:flex;justify-content:flex-end;gap:.5rem;margin-block-start:.5rem}\n"] }]
3785
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
3786
+ type: Inject,
3787
+ args: [MB_DIALOG_DATA]
3788
+ }] }, { type: MbDialogRef }] });
3789
+
3790
+ class MbConfirmService {
3791
+ dialog;
3792
+ messages = inject(MB_UI_MESSAGES);
3793
+ constructor(dialog) {
3794
+ this.dialog = dialog;
3795
+ }
3796
+ async confirm(options) {
3797
+ const ref = this.dialog.open(MbConfirmDialogComponent, {
3798
+ data: options,
3799
+ width: options.width ?? '420px',
3800
+ closeOnBackdropClick: options.closeOnBackdropClick ?? true,
3801
+ ariaLabel: options.title ?? this.messages.confirm.ariaLabel,
3802
+ });
3803
+ return (await firstValueFrom(ref.afterClosed$)) === true;
3804
+ }
3805
+ danger(message, title) {
3806
+ const confirmMessages = this.messages.confirm;
3807
+ return this.confirm({
3808
+ title: title ?? confirmMessages.deleteTitle,
3809
+ message,
3810
+ tone: 'danger',
3811
+ okText: confirmMessages.deleteOkText,
3812
+ cancelText: confirmMessages.cancelText,
3813
+ });
3814
+ }
3815
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbConfirmService, deps: [{ token: MbDialogService }], target: i0.ɵɵFactoryTarget.Injectable });
3816
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbConfirmService, providedIn: 'root' });
3817
+ }
3818
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MbConfirmService, decorators: [{
3819
+ type: Injectable,
3820
+ args: [{ providedIn: 'root' }]
3821
+ }], ctorParameters: () => [{ type: MbDialogService }] });
3822
+
3823
+ /**
3824
+ * Generated bundle index. Do not edit.
3825
+ */
3826
+
3827
+ export { MB_DIALOG_DATA, MB_GRID_DEFAULT_MESSAGES_FA, MB_GRID_MESSAGES, MB_GRID_MESSAGES_EN, MB_GRID_MESSAGES_FA, MB_JALALI_MONTH_NAMES, MB_UI_CONFIG, MB_UI_DEFAULT_CONFIG, MB_UI_MESSAGES, MB_UI_MESSAGES_EN, MB_UI_MESSAGES_FA, MbAccordionComponent, MbAlertComponent, MbAvatarComponent, MbBadgeComponent, MbBreadcrumbComponent, MbButtonComponent, MbCardComponent, MbCheckboxComponent, MbChipComponent, MbComboboxComponent, MbConfirmDialogComponent, MbConfirmService, MbDatepickerComponent, MbDialogComponent, MbDialogContainerComponent, MbDialogRef, MbDialogService, MbDividerComponent, MbDrawerComponent, MbDropdownComponent, MbEmptyComponent, MbFormFieldComponent, MbGridCellTemplateDirective, MbGridComponent, MbGridDetailTemplateDirective, MbGridEditTemplateDirective, MbGridFooterTemplateDirective, MbGridHeaderTemplateDirective, MbGridStateService, MbGridToolbarTemplateDirective, MbInputComponent, MbPaginationComponent, MbProgressComponent, MbRadioGroupComponent, MbSelectComponent, MbSkeletonComponent, MbSpinnerComponent, MbSwitchComponent, MbTabsComponent, MbTextareaComponent, MbToastContainerComponent, MbToastService, MbTooltipDirective, coerceBooleanProperty, getMbUiMessages, isNil, mbFormatJalali, mbGregorianToJalali, mbGridApplyFilter, mbGridApplySort, mbGridBuildFilter, mbGridDefaultState, mbGridDownloadBlob, mbGridExportCsv, mbGridExportExcelHtml, mbGridExportJson, mbGridFormatValue, mbGridGetValue, mbGridNormalize, mbGridProcess, mbGridStateFromLegacy, mbGridStateToLegacy, mbGridStateToNestedQuery, mbGridToLaravelQuery, mbGridToODataQuery, mbJalaliMonthLength, mbJalaliToGregorian, mbParseIsoDate, mbToIsoDate, mergeMbUiMessages, normalizeMbUiLocale, normalizeText, provideMbUi };
3828
+ //# sourceMappingURL=beshkari-mb-ui.mjs.map