cerxos-ui 0.0.3 → 0.0.5

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.
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Component, EventEmitter, Output, Input, ChangeDetectionStrategy, Attribute, forwardRef, HostListener, Directive } from '@angular/core';
2
+ import { Component, EventEmitter, Output, Input, ChangeDetectionStrategy, Attribute, forwardRef, Directive, ContentChildren, ViewChild, HostListener, ElementRef, ViewChildren } from '@angular/core';
3
3
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
4
- import { NgIf } from '@angular/common';
4
+ import { NgTemplateOutlet, NgIf } from '@angular/common';
5
5
 
6
6
  class CerxosUi {
7
7
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CerxosUi, deps: [], target: i0.ɵɵFactoryTarget.Component });
@@ -20,9 +20,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
20
20
  ` }]
21
21
  }] });
22
22
 
23
- const BASE_CLASSES$7 = 'relative flex w-full gap-3 rounded-[var(--cxs-radius-md)] border border-l-4 px-4 py-3 text-sm ' +
23
+ const BASE_CLASSES$a = 'relative flex w-full gap-3 rounded-[var(--cxs-radius-md)] border border-l-4 px-4 py-3 text-sm ' +
24
24
  'text-[var(--cxs-color-on-surface)]';
25
- const VARIANT_CLASSES$6 = {
25
+ const VARIANT_CLASSES$7 = {
26
26
  info: 'border-[var(--cxs-color-primary)] border-l-[var(--cxs-color-primary)] ' +
27
27
  'bg-[var(--cxs-color-primary-ghost)]',
28
28
  neutral: 'border-[var(--cxs-color-border)] border-l-[var(--cxs-color-border)] ' +
@@ -36,7 +36,7 @@ const TITLE_VARIANT = {
36
36
  neutral: 'text-[var(--cxs-color-on-surface)]',
37
37
  danger: 'text-[var(--cxs-color-danger)]'
38
38
  };
39
- const CLOSE_BUTTON_CLASSES = 'ml-auto inline-flex h-8 w-8 items-center justify-center rounded-[var(--cxs-radius-md)] ' +
39
+ const CLOSE_BUTTON_CLASSES$2 = 'ml-auto inline-flex h-8 w-8 items-center justify-center rounded-[var(--cxs-radius-md)] ' +
40
40
  'text-[var(--cxs-color-on-surface)] transition-colors hover:bg-[var(--cxs-color-surface-hover)] ' +
41
41
  'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
42
42
  'focus-visible:outline-[var(--cxs-color-focus)]';
@@ -47,24 +47,24 @@ class CxsAlertComponent {
47
47
  closed = new EventEmitter();
48
48
  isVisible = true;
49
49
  get alertClass() {
50
- return [BASE_CLASSES$7, VARIANT_CLASSES$6[this.variant]].join(' ');
50
+ return [BASE_CLASSES$a, VARIANT_CLASSES$7[this.variant]].join(' ');
51
51
  }
52
52
  get titleClass() {
53
53
  return [TITLE_BASE, TITLE_VARIANT[this.variant]].join(' ');
54
54
  }
55
55
  get closeButtonClass() {
56
- return CLOSE_BUTTON_CLASSES;
56
+ return CLOSE_BUTTON_CLASSES$2;
57
57
  }
58
58
  onClose() {
59
59
  this.isVisible = false;
60
60
  this.closed.emit();
61
61
  }
62
62
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsAlertComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
63
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CxsAlertComponent, isStandalone: true, selector: "cxs-alert", inputs: { variant: "variant", title: "title", dismissible: "dismissible" }, outputs: { closed: "closed" }, ngImport: i0, template: "@if (isVisible) {\n <div [class]=\"alertClass\" role=\"status\" aria-live=\"polite\">\n <div class=\"flex flex-col gap-1\">\n @if (title) {\n <div [class]=\"titleClass\">{{ title }}</div>\n }\n <div class=\"text-[var(--cxs-color-on-surface)]\">\n <ng-content></ng-content>\n </div>\n </div>\n @if (dismissible) {\n <button\n type=\"button\"\n [class]=\"closeButtonClass\"\n aria-label=\"Dismiss alert\"\n (click)=\"onClose()\"\n >\n x\n </button>\n }\n </div>\n}\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
63
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CxsAlertComponent, isStandalone: true, selector: "cxs-alert", inputs: { variant: "variant", title: "title", dismissible: "dismissible" }, outputs: { closed: "closed" }, ngImport: i0, template: "@if (isVisible) {\r\n <div [class]=\"alertClass\" role=\"status\" aria-live=\"polite\">\r\n <div class=\"flex flex-col gap-1\">\r\n @if (title) {\r\n <div [class]=\"titleClass\">{{ title }}</div>\r\n }\r\n <div class=\"text-[var(--cxs-color-on-surface)]\">\r\n <ng-content></ng-content>\r\n </div>\r\n </div>\r\n @if (dismissible) {\r\n <button\r\n type=\"button\"\r\n [class]=\"closeButtonClass\"\r\n aria-label=\"Dismiss alert\"\r\n (click)=\"onClose()\"\r\n >\r\n x\r\n </button>\r\n }\r\n </div>\r\n}\r\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
64
64
  }
65
65
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsAlertComponent, decorators: [{
66
66
  type: Component,
67
- args: [{ selector: 'cxs-alert', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (isVisible) {\n <div [class]=\"alertClass\" role=\"status\" aria-live=\"polite\">\n <div class=\"flex flex-col gap-1\">\n @if (title) {\n <div [class]=\"titleClass\">{{ title }}</div>\n }\n <div class=\"text-[var(--cxs-color-on-surface)]\">\n <ng-content></ng-content>\n </div>\n </div>\n @if (dismissible) {\n <button\n type=\"button\"\n [class]=\"closeButtonClass\"\n aria-label=\"Dismiss alert\"\n (click)=\"onClose()\"\n >\n x\n </button>\n }\n </div>\n}\n" }]
67
+ args: [{ selector: 'cxs-alert', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (isVisible) {\r\n <div [class]=\"alertClass\" role=\"status\" aria-live=\"polite\">\r\n <div class=\"flex flex-col gap-1\">\r\n @if (title) {\r\n <div [class]=\"titleClass\">{{ title }}</div>\r\n }\r\n <div class=\"text-[var(--cxs-color-on-surface)]\">\r\n <ng-content></ng-content>\r\n </div>\r\n </div>\r\n @if (dismissible) {\r\n <button\r\n type=\"button\"\r\n [class]=\"closeButtonClass\"\r\n aria-label=\"Dismiss alert\"\r\n (click)=\"onClose()\"\r\n >\r\n x\r\n </button>\r\n }\r\n </div>\r\n}\r\n" }]
68
68
  }], propDecorators: { variant: [{
69
69
  type: Input
70
70
  }], title: [{
@@ -75,9 +75,106 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
75
75
  type: Output
76
76
  }] } });
77
77
 
78
- const BASE_CLASSES$6 = 'inline-flex items-center gap-1 rounded-[var(--cxs-radius-md)] border px-2.5 py-0.5 text-xs font-medium ' +
78
+ const BASE_CLASSES$9 = 'grid';
79
+ const SPACING_CLASSES = {
80
+ sm: 'gap-2',
81
+ md: 'gap-3',
82
+ lg: 'gap-4'
83
+ };
84
+ class CxsAccordionComponent {
85
+ spacing = 'md';
86
+ get accordionClass() {
87
+ return [BASE_CLASSES$9, SPACING_CLASSES[this.spacing]].join(' ');
88
+ }
89
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsAccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
90
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: CxsAccordionComponent, isStandalone: true, selector: "cxs-accordion", inputs: { spacing: "spacing" }, ngImport: i0, template: "<div [class]=\"accordionClass\">\n <ng-content></ng-content>\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
91
+ }
92
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsAccordionComponent, decorators: [{
93
+ type: Component,
94
+ args: [{ selector: 'cxs-accordion', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class]=\"accordionClass\">\n <ng-content></ng-content>\n</div>\n" }]
95
+ }], propDecorators: { spacing: [{
96
+ type: Input
97
+ }] } });
98
+
99
+ const ITEM_BASE = 'rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
100
+ 'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)]';
101
+ const ITEM_DISABLED = 'opacity-60';
102
+ const HEADER_BASE = 'flex w-full items-center justify-between gap-3 text-left font-medium transition-colors ' +
103
+ 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
104
+ 'focus-visible:outline-[var(--cxs-color-focus)]';
105
+ const HEADER_HOVER = 'hover:bg-[var(--cxs-color-surface-hover)]';
106
+ const HEADER_DISABLED = 'cursor-not-allowed';
107
+ const CONTENT_BASE = 'text-(--cxs-color-on-surface-muted)';
108
+ const SIZE_HEADER_CLASSES = {
109
+ sm: 'px-3 py-2 text-sm',
110
+ md: 'px-4 py-3 text-sm',
111
+ lg: 'px-5 py-4 text-base'
112
+ };
113
+ const SIZE_CONTENT_CLASSES = {
114
+ sm: 'px-3 pb-3 text-sm',
115
+ md: 'px-4 pb-4 text-sm',
116
+ lg: 'px-5 pb-5 text-base'
117
+ };
118
+ class CxsAccordionItemComponent {
119
+ static nextId = 0;
120
+ title;
121
+ expanded = false;
122
+ disabled = false;
123
+ size = 'md';
124
+ expandedChange = new EventEmitter();
125
+ buttonId = `cxs-accordion-button-${++CxsAccordionItemComponent.nextId}`;
126
+ contentId = `cxs-accordion-panel-${CxsAccordionItemComponent.nextId}`;
127
+ get itemClass() {
128
+ return [ITEM_BASE, this.disabled ? ITEM_DISABLED : ''].filter(Boolean).join(' ');
129
+ }
130
+ get headerClass() {
131
+ return [
132
+ HEADER_BASE,
133
+ SIZE_HEADER_CLASSES[this.size],
134
+ this.disabled ? HEADER_DISABLED : HEADER_HOVER
135
+ ]
136
+ .filter(Boolean)
137
+ .join(' ');
138
+ }
139
+ get contentClass() {
140
+ return [CONTENT_BASE, SIZE_CONTENT_CLASSES[this.size]].join(' ');
141
+ }
142
+ get iconClass() {
143
+ return [
144
+ 'h-4 w-4 shrink-0 text-(--cxs-color-on-surface-muted) transition-transform',
145
+ this.expanded ? 'rotate-180' : ''
146
+ ]
147
+ .filter(Boolean)
148
+ .join(' ');
149
+ }
150
+ toggle() {
151
+ if (this.disabled) {
152
+ return;
153
+ }
154
+ this.expanded = !this.expanded;
155
+ this.expandedChange.emit(this.expanded);
156
+ }
157
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsAccordionItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
158
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CxsAccordionItemComponent, isStandalone: true, selector: "cxs-accordion-item", inputs: { title: "title", expanded: "expanded", disabled: "disabled", size: "size" }, outputs: { expandedChange: "expandedChange" }, ngImport: i0, template: "<div [class]=\"itemClass\">\n <button\n type=\"button\"\n [class]=\"headerClass\"\n [id]=\"buttonId\"\n [disabled]=\"disabled\"\n [attr.aria-expanded]=\"expanded\"\n [attr.aria-controls]=\"contentId\"\n (click)=\"toggle()\"\n >\n <span class=\"flex min-w-0 flex-1 items-center gap-2\">\n <ng-content select=\"[cxsAccordionTitle]\"></ng-content>\n @if (title) {\n <span class=\"truncate\">{{ title }}</span>\n }\n </span>\n <svg aria-hidden=\"true\" [class]=\"iconClass\" viewBox=\"0 0 20 20\">\n <path\n d=\"M5 7l5 5 5-5\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.5\"\n ></path>\n </svg>\n </button>\n\n <div\n [id]=\"contentId\"\n role=\"region\"\n [attr.aria-labelledby]=\"buttonId\"\n [class]=\"contentClass\"\n [hidden]=\"!expanded\"\n >\n <ng-content></ng-content>\n </div>\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
159
+ }
160
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsAccordionItemComponent, decorators: [{
161
+ type: Component,
162
+ args: [{ selector: 'cxs-accordion-item', standalone: true, imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class]=\"itemClass\">\n <button\n type=\"button\"\n [class]=\"headerClass\"\n [id]=\"buttonId\"\n [disabled]=\"disabled\"\n [attr.aria-expanded]=\"expanded\"\n [attr.aria-controls]=\"contentId\"\n (click)=\"toggle()\"\n >\n <span class=\"flex min-w-0 flex-1 items-center gap-2\">\n <ng-content select=\"[cxsAccordionTitle]\"></ng-content>\n @if (title) {\n <span class=\"truncate\">{{ title }}</span>\n }\n </span>\n <svg aria-hidden=\"true\" [class]=\"iconClass\" viewBox=\"0 0 20 20\">\n <path\n d=\"M5 7l5 5 5-5\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.5\"\n ></path>\n </svg>\n </button>\n\n <div\n [id]=\"contentId\"\n role=\"region\"\n [attr.aria-labelledby]=\"buttonId\"\n [class]=\"contentClass\"\n [hidden]=\"!expanded\"\n >\n <ng-content></ng-content>\n </div>\n</div>\n" }]
163
+ }], propDecorators: { title: [{
164
+ type: Input
165
+ }], expanded: [{
166
+ type: Input
167
+ }], disabled: [{
168
+ type: Input
169
+ }], size: [{
170
+ type: Input
171
+ }], expandedChange: [{
172
+ type: Output
173
+ }] } });
174
+
175
+ const BASE_CLASSES$8 = 'inline-flex items-center gap-1 rounded-[var(--cxs-radius-md)] border px-2.5 py-0.5 text-xs font-medium ' +
79
176
  'leading-none';
80
- const VARIANT_CLASSES$5 = {
177
+ const VARIANT_CLASSES$6 = {
81
178
  primary: 'border-[var(--cxs-color-primary)] bg-[var(--cxs-color-primary-ghost)] ' +
82
179
  'text-[var(--cxs-color-primary)]',
83
180
  neutral: 'border-[var(--cxs-color-border)] bg-[var(--cxs-color-surface)] ' +
@@ -85,7 +182,7 @@ const VARIANT_CLASSES$5 = {
85
182
  danger: 'border-[var(--cxs-color-danger)] bg-[var(--cxs-color-surface)] ' +
86
183
  'text-[var(--cxs-color-danger)]'
87
184
  };
88
- const SIZE_CLASSES$6 = {
185
+ const SIZE_CLASSES$7 = {
89
186
  sm: 'text-[11px]',
90
187
  md: 'text-xs'
91
188
  };
@@ -93,21 +190,124 @@ class CxsBadgeComponent {
93
190
  variant = 'primary';
94
191
  size = 'md';
95
192
  get badgeClass() {
96
- return [BASE_CLASSES$6, VARIANT_CLASSES$5[this.variant], SIZE_CLASSES$6[this.size]].join(' ');
193
+ return [BASE_CLASSES$8, VARIANT_CLASSES$6[this.variant], SIZE_CLASSES$7[this.size]].join(' ');
97
194
  }
98
195
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
99
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: CxsBadgeComponent, isStandalone: true, selector: "cxs-badge", inputs: { variant: "variant", size: "size" }, ngImport: i0, template: "<span [class]=\"badgeClass\">\n <ng-content></ng-content>\n</span>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
196
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: CxsBadgeComponent, isStandalone: true, selector: "cxs-badge", inputs: { variant: "variant", size: "size" }, ngImport: i0, template: "<span [class]=\"badgeClass\">\r\n <ng-content></ng-content>\r\n</span>\r\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
100
197
  }
101
198
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsBadgeComponent, decorators: [{
102
199
  type: Component,
103
- args: [{ selector: 'cxs-badge', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<span [class]=\"badgeClass\">\n <ng-content></ng-content>\n</span>\n" }]
200
+ args: [{ selector: 'cxs-badge', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<span [class]=\"badgeClass\">\r\n <ng-content></ng-content>\r\n</span>\r\n" }]
104
201
  }], propDecorators: { variant: [{
105
202
  type: Input
106
203
  }], size: [{
107
204
  type: Input
108
205
  }] } });
109
206
 
110
- const BASE_CLASSES$5 = 'flex flex-wrap items-center gap-x-1 gap-y-1';
207
+ const BASE_CLASSES$7 = 'inline-flex items-center justify-center overflow-hidden border border-[var(--cxs-color-border)] ' +
208
+ 'bg-[var(--cxs-color-primary-ghost)] text-[var(--cxs-color-primary)] font-semibold uppercase ' +
209
+ 'select-none';
210
+ const SIZE_CLASSES$6 = {
211
+ sm: 'h-8 w-8 text-[11px]',
212
+ md: 'h-10 w-10 text-xs',
213
+ lg: 'h-14 w-14 text-sm'
214
+ };
215
+ const SHAPE_CLASSES = {
216
+ circle: 'rounded-full',
217
+ square: 'rounded-[var(--cxs-radius-md)]'
218
+ };
219
+ const FALLBACK_CLASSES = 'leading-none';
220
+ class CxsAvatarComponent {
221
+ hostClass;
222
+ _src;
223
+ set src(value) {
224
+ if (value !== this._src) {
225
+ this._src = value;
226
+ this.imageError = false;
227
+ }
228
+ }
229
+ get src() {
230
+ return this._src;
231
+ }
232
+ name;
233
+ alt;
234
+ size = 'md';
235
+ shape = 'circle';
236
+ imageError = false;
237
+ constructor(hostClass) {
238
+ this.hostClass = hostClass;
239
+ }
240
+ get avatarClass() {
241
+ return [BASE_CLASSES$7, SIZE_CLASSES$6[this.size], SHAPE_CLASSES[this.shape], this.hostClass]
242
+ .filter(Boolean)
243
+ .join(' ');
244
+ }
245
+ get fallbackClass() {
246
+ return FALLBACK_CLASSES;
247
+ }
248
+ get altText() {
249
+ if (this.alt !== undefined) {
250
+ return this.alt;
251
+ }
252
+ if (this.name) {
253
+ return this.name;
254
+ }
255
+ return 'Avatar';
256
+ }
257
+ get fallbackLabel() {
258
+ if (this.name) {
259
+ return this.name;
260
+ }
261
+ if (this.alt && this.alt.trim().length > 0) {
262
+ return this.alt;
263
+ }
264
+ return 'Avatar';
265
+ }
266
+ get fallbackText() {
267
+ if (!this.name) {
268
+ return '?';
269
+ }
270
+ return this.getInitials(this.name);
271
+ }
272
+ get showImage() {
273
+ return !!this.src && !this.imageError;
274
+ }
275
+ onImageError() {
276
+ this.imageError = true;
277
+ }
278
+ getInitials(value) {
279
+ const parts = value
280
+ .trim()
281
+ .split(/\s+/)
282
+ .filter(Boolean);
283
+ if (parts.length >= 2) {
284
+ return `${parts[0][0]}${parts[1][0]}`.toUpperCase();
285
+ }
286
+ const word = parts[0] ?? '';
287
+ return word.slice(0, 2).toUpperCase() || '?';
288
+ }
289
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsAvatarComponent, deps: [{ token: 'class', attribute: true }], target: i0.ɵɵFactoryTarget.Component });
290
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CxsAvatarComponent, isStandalone: true, selector: "cxs-avatar", inputs: { src: "src", name: "name", alt: "alt", size: "size", shape: "shape" }, ngImport: i0, template: "<div\n [class]=\"avatarClass\"\n [attr.role]=\"showImage ? null : 'img'\"\n [attr.aria-label]=\"showImage ? null : fallbackLabel\"\n>\n @if (showImage) {\n <img\n [attr.src]=\"src\"\n [attr.alt]=\"altText\"\n class=\"h-full w-full object-cover\"\n (error)=\"onImageError()\"\n />\n } @else {\n <span [class]=\"fallbackClass\" aria-hidden=\"true\">{{ fallbackText }}</span>\n }\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
291
+ }
292
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsAvatarComponent, decorators: [{
293
+ type: Component,
294
+ args: [{ selector: 'cxs-avatar', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n [class]=\"avatarClass\"\n [attr.role]=\"showImage ? null : 'img'\"\n [attr.aria-label]=\"showImage ? null : fallbackLabel\"\n>\n @if (showImage) {\n <img\n [attr.src]=\"src\"\n [attr.alt]=\"altText\"\n class=\"h-full w-full object-cover\"\n (error)=\"onImageError()\"\n />\n } @else {\n <span [class]=\"fallbackClass\" aria-hidden=\"true\">{{ fallbackText }}</span>\n }\n</div>\n" }]
295
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
296
+ type: Attribute,
297
+ args: ['class']
298
+ }] }], propDecorators: { src: [{
299
+ type: Input
300
+ }], name: [{
301
+ type: Input
302
+ }], alt: [{
303
+ type: Input
304
+ }], size: [{
305
+ type: Input
306
+ }], shape: [{
307
+ type: Input
308
+ }] } });
309
+
310
+ const BASE_CLASSES$6 = 'flex flex-wrap items-center gap-x-1 gap-y-1';
111
311
  const ITEM_CLASSES = 'flex items-center gap-1';
112
312
  const LINK_BASE_CLASSES = 'rounded-[var(--cxs-radius-md)] transition-colors focus-visible:outline focus-visible:outline-2 ' +
113
313
  'focus-visible:outline-offset-2 focus-visible:outline-[var(--cxs-color-focus)]';
@@ -135,7 +335,7 @@ class CxsBreadcrumbComponent {
135
335
  variant = 'default';
136
336
  disabled = false;
137
337
  get listClass() {
138
- return [BASE_CLASSES$5, SIZE_CLASSES$5[this.size], this.disabled ? 'opacity-60' : '']
338
+ return [BASE_CLASSES$6, SIZE_CLASSES$5[this.size], this.disabled ? 'opacity-60' : '']
139
339
  .filter(Boolean)
140
340
  .join(' ');
141
341
  }
@@ -177,11 +377,11 @@ class CxsBreadcrumbComponent {
177
377
  return item.rel ?? null;
178
378
  }
179
379
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsBreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
180
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CxsBreadcrumbComponent, isStandalone: true, selector: "cxs-breadcrumb", inputs: { items: "items", separator: "separator", ariaLabel: "ariaLabel", size: "size", variant: "variant", disabled: "disabled" }, ngImport: i0, template: "<nav [attr.aria-label]=\"ariaLabel\">\n <ol [class]=\"listClass\">\n @for (item of items; track item; let last = $last) {\n <li [class]=\"itemClass\">\n @if (isLink(item, last)) {\n <a\n [class]=\"getLinkClass(item)\"\n [attr.href]=\"item.href\"\n [attr.target]=\"item.target ?? null\"\n [attr.rel]=\"getItemRel(item)\"\n [attr.aria-label]=\"item.ariaLabel ?? null\"\n >\n {{ item.label }}\n </a>\n } @else {\n <span\n [class]=\"getLabelClass(item, last)\"\n [attr.aria-current]=\"last ? 'page' : null\"\n >\n {{ item.label }}\n </span>\n }\n @if (!last && separator) {\n <span [class]=\"separatorClass\" aria-hidden=\"true\">{{ separator }}</span>\n }\n </li>\n }\n </ol>\n</nav>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
380
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CxsBreadcrumbComponent, isStandalone: true, selector: "cxs-breadcrumb", inputs: { items: "items", separator: "separator", ariaLabel: "ariaLabel", size: "size", variant: "variant", disabled: "disabled" }, ngImport: i0, template: "<nav [attr.aria-label]=\"ariaLabel\">\r\n <ol [class]=\"listClass\">\r\n @for (item of items; track item; let last = $last) {\r\n <li [class]=\"itemClass\">\r\n @if (isLink(item, last)) {\r\n <a\r\n [class]=\"getLinkClass(item)\"\r\n [attr.href]=\"item.href\"\r\n [attr.target]=\"item.target ?? null\"\r\n [attr.rel]=\"getItemRel(item)\"\r\n [attr.aria-label]=\"item.ariaLabel ?? null\"\r\n >\r\n {{ item.label }}\r\n </a>\r\n } @else {\r\n <span\r\n [class]=\"getLabelClass(item, last)\"\r\n [attr.aria-current]=\"last ? 'page' : null\"\r\n >\r\n {{ item.label }}\r\n </span>\r\n }\r\n @if (!last && separator) {\r\n <span [class]=\"separatorClass\" aria-hidden=\"true\">{{ separator }}</span>\r\n }\r\n </li>\r\n }\r\n </ol>\r\n</nav>\r\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
181
381
  }
182
382
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsBreadcrumbComponent, decorators: [{
183
383
  type: Component,
184
- args: [{ selector: 'cxs-breadcrumb', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav [attr.aria-label]=\"ariaLabel\">\n <ol [class]=\"listClass\">\n @for (item of items; track item; let last = $last) {\n <li [class]=\"itemClass\">\n @if (isLink(item, last)) {\n <a\n [class]=\"getLinkClass(item)\"\n [attr.href]=\"item.href\"\n [attr.target]=\"item.target ?? null\"\n [attr.rel]=\"getItemRel(item)\"\n [attr.aria-label]=\"item.ariaLabel ?? null\"\n >\n {{ item.label }}\n </a>\n } @else {\n <span\n [class]=\"getLabelClass(item, last)\"\n [attr.aria-current]=\"last ? 'page' : null\"\n >\n {{ item.label }}\n </span>\n }\n @if (!last && separator) {\n <span [class]=\"separatorClass\" aria-hidden=\"true\">{{ separator }}</span>\n }\n </li>\n }\n </ol>\n</nav>\n" }]
384
+ args: [{ selector: 'cxs-breadcrumb', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav [attr.aria-label]=\"ariaLabel\">\r\n <ol [class]=\"listClass\">\r\n @for (item of items; track item; let last = $last) {\r\n <li [class]=\"itemClass\">\r\n @if (isLink(item, last)) {\r\n <a\r\n [class]=\"getLinkClass(item)\"\r\n [attr.href]=\"item.href\"\r\n [attr.target]=\"item.target ?? null\"\r\n [attr.rel]=\"getItemRel(item)\"\r\n [attr.aria-label]=\"item.ariaLabel ?? null\"\r\n >\r\n {{ item.label }}\r\n </a>\r\n } @else {\r\n <span\r\n [class]=\"getLabelClass(item, last)\"\r\n [attr.aria-current]=\"last ? 'page' : null\"\r\n >\r\n {{ item.label }}\r\n </span>\r\n }\r\n @if (!last && separator) {\r\n <span [class]=\"separatorClass\" aria-hidden=\"true\">{{ separator }}</span>\r\n }\r\n </li>\r\n }\r\n </ol>\r\n</nav>\r\n" }]
185
385
  }], propDecorators: { items: [{
186
386
  type: Input
187
387
  }], separator: [{
@@ -196,10 +396,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
196
396
  type: Input
197
397
  }] } });
198
398
 
199
- const BASE_CLASSES$4 = 'relative inline-flex items-center justify-center gap-2 whitespace-nowrap font-medium transition-colors ' +
399
+ const BASE_CLASSES$5 = 'relative inline-flex items-center justify-center gap-2 whitespace-nowrap font-medium transition-colors ' +
200
400
  'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
201
401
  'focus-visible:outline-[var(--cxs-color-focus)] disabled:pointer-events-none disabled:opacity-50';
202
- const VARIANT_CLASSES$4 = {
402
+ const VARIANT_CLASSES$5 = {
203
403
  primary: 'bg-[var(--cxs-color-primary)] text-[var(--cxs-color-on-primary)] ' +
204
404
  'hover:bg-[var(--cxs-color-primary-hover)] shadow-[var(--cxs-shadow-sm)]',
205
405
  secondary: 'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)] ' +
@@ -225,16 +425,16 @@ class CxsButtonComponent {
225
425
  return this.disabled || this.loading;
226
426
  }
227
427
  get buttonClass() {
228
- return [BASE_CLASSES$4, SIZE_CLASSES$4[this.size], VARIANT_CLASSES$4[this.variant], this.hostClass]
428
+ return [BASE_CLASSES$5, SIZE_CLASSES$4[this.size], VARIANT_CLASSES$5[this.variant], this.hostClass]
229
429
  .filter(Boolean)
230
430
  .join(' ');
231
431
  }
232
432
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsButtonComponent, deps: [{ token: 'class', attribute: true }], target: i0.ɵɵFactoryTarget.Component });
233
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CxsButtonComponent, isStandalone: true, selector: "cxs-button", inputs: { variant: "variant", size: "size", disabled: "disabled", loading: "loading", type: "type" }, ngImport: i0, template: "<button\n [class]=\"buttonClass\"\n [attr.type]=\"type\"\n [disabled]=\"isDisabled\"\n [attr.aria-busy]=\"loading ? 'true' : null\"\n>\n @if (loading) {\n <span\n class=\"h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent\"\n aria-hidden=\"true\"\n ></span>\n }\n <span class=\"truncate\">\n <ng-content></ng-content>\n </span>\n</button>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
433
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CxsButtonComponent, isStandalone: true, selector: "cxs-button", inputs: { variant: "variant", size: "size", disabled: "disabled", loading: "loading", type: "type" }, ngImport: i0, template: "<button\r\n [class]=\"buttonClass\"\r\n [attr.type]=\"type\"\r\n [disabled]=\"isDisabled\"\r\n [attr.aria-busy]=\"loading ? 'true' : null\"\r\n>\r\n @if (loading) {\n <span\n class=\"h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent\"\n aria-hidden=\"true\"\n ></span>\n }\n <ng-content select=\"[cxsButtonIcon]\"></ng-content>\n <span class=\"truncate\">\n <ng-content></ng-content>\n </span>\n</button>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
234
434
  }
235
435
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsButtonComponent, decorators: [{
236
436
  type: Component,
237
- args: [{ selector: 'cxs-button', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n [class]=\"buttonClass\"\n [attr.type]=\"type\"\n [disabled]=\"isDisabled\"\n [attr.aria-busy]=\"loading ? 'true' : null\"\n>\n @if (loading) {\n <span\n class=\"h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent\"\n aria-hidden=\"true\"\n ></span>\n }\n <span class=\"truncate\">\n <ng-content></ng-content>\n </span>\n</button>\n" }]
437
+ args: [{ selector: 'cxs-button', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\r\n [class]=\"buttonClass\"\r\n [attr.type]=\"type\"\r\n [disabled]=\"isDisabled\"\r\n [attr.aria-busy]=\"loading ? 'true' : null\"\r\n>\r\n @if (loading) {\n <span\n class=\"h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent\"\n aria-hidden=\"true\"\n ></span>\n }\n <ng-content select=\"[cxsButtonIcon]\"></ng-content>\n <span class=\"truncate\">\n <ng-content></ng-content>\n </span>\n</button>\n" }]
238
438
  }], ctorParameters: () => [{ type: undefined, decorators: [{
239
439
  type: Attribute,
240
440
  args: ['class']
@@ -250,9 +450,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
250
450
  type: Input
251
451
  }] } });
252
452
 
253
- const BASE_CLASSES$3 = 'rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
453
+ const BASE_CLASSES$4 = 'rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
254
454
  'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)]';
255
- const VARIANT_CLASSES$3 = {
455
+ const VARIANT_CLASSES$4 = {
256
456
  surface: 'bg-[var(--cxs-color-surface)]',
257
457
  muted: 'bg-[var(--cxs-color-surface-hover)]',
258
458
  outline: 'bg-transparent'
@@ -270,8 +470,8 @@ class CxsCardComponent {
270
470
  ariaLabelledby;
271
471
  get cardClass() {
272
472
  return [
273
- BASE_CLASSES$3,
274
- VARIANT_CLASSES$3[this.variant],
473
+ BASE_CLASSES$4,
474
+ VARIANT_CLASSES$4[this.variant],
275
475
  SIZE_CLASSES$3[this.size],
276
476
  this.elevated ? 'shadow-[var(--cxs-shadow-sm)]' : ''
277
477
  ]
@@ -279,11 +479,11 @@ class CxsCardComponent {
279
479
  .join(' ');
280
480
  }
281
481
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
282
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: CxsCardComponent, isStandalone: true, selector: "cxs-card", inputs: { variant: "variant", size: "size", elevated: "elevated", ariaLabel: "ariaLabel", ariaLabelledby: "ariaLabelledby" }, ngImport: i0, template: "<section\n [class]=\"cardClass\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-labelledby]=\"ariaLabelledby\"\n>\n <ng-content></ng-content>\n</section>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
482
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: CxsCardComponent, isStandalone: true, selector: "cxs-card", inputs: { variant: "variant", size: "size", elevated: "elevated", ariaLabel: "ariaLabel", ariaLabelledby: "ariaLabelledby" }, ngImport: i0, template: "<section\r\n [class]=\"cardClass\"\r\n [attr.aria-label]=\"ariaLabel\"\r\n [attr.aria-labelledby]=\"ariaLabelledby\"\r\n>\r\n <ng-content></ng-content>\r\n</section>\r\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
283
483
  }
284
484
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsCardComponent, decorators: [{
285
485
  type: Component,
286
- args: [{ selector: 'cxs-card', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<section\n [class]=\"cardClass\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-labelledby]=\"ariaLabelledby\"\n>\n <ng-content></ng-content>\n</section>\n" }]
486
+ args: [{ selector: 'cxs-card', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<section\r\n [class]=\"cardClass\"\r\n [attr.aria-label]=\"ariaLabel\"\r\n [attr.aria-labelledby]=\"ariaLabelledby\"\r\n>\r\n <ng-content></ng-content>\r\n</section>\r\n" }]
287
487
  }], propDecorators: { variant: [{
288
488
  type: Input
289
489
  }], size: [{
@@ -401,7 +601,7 @@ class CxsCheckboxComponent {
401
601
  useExisting: forwardRef(() => CxsCheckboxComponent),
402
602
  multi: true
403
603
  }
404
- ], ngImport: i0, template: "<label [class]=\"wrapperClass\">\n <input\n class=\"peer sr-only\"\n type=\"checkbox\"\n [attr.id]=\"id\"\n [attr.name]=\"name\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-describedby]=\"ariaDescribedby\"\n [attr.aria-checked]=\"ariaChecked\"\n [attr.autofocus]=\"autofocus ? true : null\"\n [checked]=\"checked\"\n [indeterminate]=\"indeterminate\"\n [disabled]=\"isDisabled\"\n [required]=\"required\"\n (change)=\"onCheckboxChange($event)\"\n (blur)=\"onBlur()\"\n />\n <span [class]=\"controlClass\" aria-hidden=\"true\"></span>\n @if (label) {\n <span [class]=\"labelClass\">{{ label }}</span>\n } @else {\n <span [class]=\"labelClass\">\n <ng-content></ng-content>\n </span>\n }\n</label>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
604
+ ], ngImport: i0, template: "<label [class]=\"wrapperClass\">\r\n <input\r\n class=\"peer sr-only\"\r\n type=\"checkbox\"\r\n [attr.id]=\"id\"\r\n [attr.name]=\"name\"\r\n [attr.aria-label]=\"ariaLabel\"\r\n [attr.aria-describedby]=\"ariaDescribedby\"\r\n [attr.aria-checked]=\"ariaChecked\"\r\n [attr.autofocus]=\"autofocus ? true : null\"\r\n [checked]=\"checked\"\r\n [indeterminate]=\"indeterminate\"\r\n [disabled]=\"isDisabled\"\r\n [required]=\"required\"\r\n (change)=\"onCheckboxChange($event)\"\r\n (blur)=\"onBlur()\"\r\n />\r\n <span [class]=\"controlClass\" aria-hidden=\"true\"></span>\r\n @if (label) {\r\n <span [class]=\"labelClass\">{{ label }}</span>\r\n } @else {\r\n <span [class]=\"labelClass\">\r\n <ng-content></ng-content>\r\n </span>\r\n }\r\n</label>\r\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
405
605
  }
406
606
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsCheckboxComponent, decorators: [{
407
607
  type: Component,
@@ -411,7 +611,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
411
611
  useExisting: forwardRef(() => CxsCheckboxComponent),
412
612
  multi: true
413
613
  }
414
- ], template: "<label [class]=\"wrapperClass\">\n <input\n class=\"peer sr-only\"\n type=\"checkbox\"\n [attr.id]=\"id\"\n [attr.name]=\"name\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-describedby]=\"ariaDescribedby\"\n [attr.aria-checked]=\"ariaChecked\"\n [attr.autofocus]=\"autofocus ? true : null\"\n [checked]=\"checked\"\n [indeterminate]=\"indeterminate\"\n [disabled]=\"isDisabled\"\n [required]=\"required\"\n (change)=\"onCheckboxChange($event)\"\n (blur)=\"onBlur()\"\n />\n <span [class]=\"controlClass\" aria-hidden=\"true\"></span>\n @if (label) {\n <span [class]=\"labelClass\">{{ label }}</span>\n } @else {\n <span [class]=\"labelClass\">\n <ng-content></ng-content>\n </span>\n }\n</label>\n" }]
614
+ ], template: "<label [class]=\"wrapperClass\">\r\n <input\r\n class=\"peer sr-only\"\r\n type=\"checkbox\"\r\n [attr.id]=\"id\"\r\n [attr.name]=\"name\"\r\n [attr.aria-label]=\"ariaLabel\"\r\n [attr.aria-describedby]=\"ariaDescribedby\"\r\n [attr.aria-checked]=\"ariaChecked\"\r\n [attr.autofocus]=\"autofocus ? true : null\"\r\n [checked]=\"checked\"\r\n [indeterminate]=\"indeterminate\"\r\n [disabled]=\"isDisabled\"\r\n [required]=\"required\"\r\n (change)=\"onCheckboxChange($event)\"\r\n (blur)=\"onBlur()\"\r\n />\r\n <span [class]=\"controlClass\" aria-hidden=\"true\"></span>\r\n @if (label) {\r\n <span [class]=\"labelClass\">{{ label }}</span>\r\n } @else {\r\n <span [class]=\"labelClass\">\r\n <ng-content></ng-content>\r\n </span>\r\n }\r\n</label>\r\n" }]
415
615
  }], propDecorators: { checked: [{
416
616
  type: Input
417
617
  }], indeterminate: [{
@@ -438,175 +638,1103 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
438
638
  type: Output
439
639
  }] } });
440
640
 
441
- const BASE_CLASSES$2 = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
442
- 'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)] ' +
443
- 'placeholder:text-[var(--cxs-color-on-surface-muted)] transition-colors ' +
444
- 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
445
- 'focus-visible:outline-[var(--cxs-color-focus)] focus-visible:border-[var(--cxs-color-focus)] ' +
446
- 'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-60';
447
- const VARIANT_CLASSES$2 = {
448
- outline: 'bg-[var(--cxs-color-surface)]',
449
- filled: 'bg-[var(--cxs-color-surface-hover)]'
450
- };
451
- const SIZE_CLASSES$2 = {
452
- sm: 'h-8 px-3 text-sm',
453
- md: 'h-10 px-3 text-sm',
454
- lg: 'h-12 px-4 text-base'
455
- };
456
- const INVALID_CLASSES$3 = 'border-[var(--cxs-color-danger)] focus-visible:border-[var(--cxs-color-danger)] ' +
457
- 'focus-visible:outline-[var(--cxs-color-danger)]';
458
- const POPOVER_CLASSES = 'absolute left-0 top-full z-10 mt-2 w-full rounded-[var(--cxs-radius-md)] border ' +
459
- 'border-[var(--cxs-color-border)] bg-[var(--cxs-color-surface)] p-3 shadow-[var(--cxs-shadow-sm)]';
460
- const NAV_BUTTON_CLASSES = 'inline-flex h-8 w-8 items-center justify-center rounded-[var(--cxs-radius-md)] ' +
461
- 'text-[var(--cxs-color-on-surface)] transition-colors hover:bg-[var(--cxs-color-surface-hover)] ' +
641
+ const WRAPPER_BASE_CLASSES$2 = 'relative overflow-hidden rounded-[var(--cxs-radius-md)]';
642
+ const TRACK_CLASSES = 'flex transition-transform duration-500 ease-out';
643
+ const SLIDE_BASE_CLASSES = 'w-full flex-shrink-0';
644
+ const IMAGE_CLASSES = 'h-56 w-full object-cover';
645
+ const CONTENT_CLASSES = 'absolute inset-x-0 bottom-0 bg-[rgba(15,23,42,0.6)] px-4 py-3 text-sm text-white';
646
+ const CONTROL_CLASSES = 'absolute top-1/2 -translate-y-1/2 inline-flex h-9 w-9 items-center justify-center rounded-full ' +
647
+ 'bg-[rgba(15,23,42,0.7)] text-white transition hover:bg-[rgba(15,23,42,0.85)] ' +
462
648
  'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
463
649
  'focus-visible:outline-[var(--cxs-color-focus)]';
464
- const DAY_BUTTON_BASE = 'flex h-8 w-8 items-center justify-center rounded-[var(--cxs-radius-md)] text-xs ' +
465
- 'transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
466
- 'focus-visible:outline-[var(--cxs-color-focus)] disabled:cursor-not-allowed disabled:opacity-40';
467
- const DAY_OUTSIDE_MONTH = 'text-[var(--cxs-color-on-surface-muted)]';
468
- const DAY_TODAY = 'border border-[var(--cxs-color-primary)]';
469
- const DAY_SELECTED = 'bg-[var(--cxs-color-primary)] text-[var(--cxs-color-on-primary)]';
470
- const DAY_HOVER = 'hover:bg-[var(--cxs-color-surface-hover)]';
471
- class CxsDatepickerComponent {
472
- value = '';
473
- variant = 'outline';
474
- size = 'md';
475
- useNative = true;
476
- disabled = false;
477
- readonly = false;
478
- required = false;
479
- invalid = false;
480
- autofocus = false;
481
- id;
482
- name;
483
- min;
484
- max;
485
- placeholder;
486
- ariaLabel;
487
- ariaDescribedby;
488
- valueChange = new EventEmitter();
489
- static nextId = 0;
490
- fallbackId = `cxs-datepicker-panel-${CxsDatepickerComponent.nextId++}`;
491
- disabledFromControl = false;
492
- onChange = () => { };
493
- onTouched = () => { };
494
- isOpen = false;
495
- displayMonth = new Date();
496
- calendarDays = [];
497
- weekDayLabels = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
498
- get isDisabled() {
499
- return this.disabled || this.disabledFromControl;
500
- }
501
- get panelId() {
502
- return this.id ? `${this.id}-panel` : this.fallbackId;
503
- }
504
- get inputClass() {
505
- return [
506
- BASE_CLASSES$2,
507
- SIZE_CLASSES$2[this.size],
508
- VARIANT_CLASSES$2[this.variant],
509
- this.invalid ? INVALID_CLASSES$3 : ''
510
- ]
511
- .filter(Boolean)
512
- .join(' ');
650
+ const INDICATOR_CONTAINER_CLASSES = 'absolute bottom-3 left-1/2 flex -translate-x-1/2 items-center gap-2';
651
+ const INDICATOR_BASE_CLASSES = 'h-2 w-2 rounded-full bg-white/50 transition-colors';
652
+ const INDICATOR_ACTIVE_CLASSES = 'bg-white';
653
+ class CxsCarouselComponent {
654
+ hostClass;
655
+ items = [];
656
+ activeIndex = 0;
657
+ autoplay = false;
658
+ interval = 4000;
659
+ loop = true;
660
+ showControls = true;
661
+ showIndicators = true;
662
+ ariaLabel = 'Carousel';
663
+ activeIndexChange = new EventEmitter();
664
+ timerId = null;
665
+ constructor(hostClass) {
666
+ this.hostClass = hostClass;
513
667
  }
514
- get popoverClass() {
515
- return POPOVER_CLASSES;
668
+ ngOnChanges(changes) {
669
+ if (changes['items'] || changes['autoplay'] || changes['interval']) {
670
+ this.resetAutoplay();
671
+ }
672
+ if (changes['activeIndex']) {
673
+ this.activeIndex = this.clampIndex(this.activeIndex);
674
+ }
516
675
  }
517
- get navButtonClass() {
518
- return NAV_BUTTON_CLASSES;
676
+ ngAfterViewInit() {
677
+ this.resetAutoplay();
519
678
  }
520
- get displayMonthLabel() {
521
- return this.displayMonth.toLocaleString('en-US', { month: 'long', year: 'numeric' });
679
+ ngOnDestroy() {
680
+ this.clearAutoplay();
522
681
  }
523
- writeValue(value) {
524
- this.value = value ?? '';
525
- this.syncDisplayMonthWithValue();
682
+ get wrapperClass() {
683
+ return [WRAPPER_BASE_CLASSES$2, this.hostClass].filter(Boolean).join(' ');
526
684
  }
527
- registerOnChange(fn) {
528
- this.onChange = fn;
685
+ get trackStyle() {
686
+ return `translateX(-${this.activeIndex * 100}%)`;
529
687
  }
530
- registerOnTouched(fn) {
531
- this.onTouched = fn;
688
+ get trackClass() {
689
+ return TRACK_CLASSES;
532
690
  }
533
- setDisabledState(isDisabled) {
534
- this.disabledFromControl = isDisabled;
691
+ get slideClass() {
692
+ return SLIDE_BASE_CLASSES;
535
693
  }
536
- onInput(event) {
537
- const target = event.target;
538
- const nextValue = target.value;
539
- this.value = nextValue;
540
- this.onChange(nextValue);
541
- this.valueChange.emit(nextValue);
694
+ get imageClass() {
695
+ return IMAGE_CLASSES;
542
696
  }
543
- onDateSelect(day) {
544
- if (day.disabled || this.isDisabled) {
545
- return;
546
- }
547
- const nextValue = this.formatDate(day.date);
548
- this.value = nextValue;
549
- this.onChange(nextValue);
550
- this.valueChange.emit(nextValue);
551
- this.isOpen = false;
697
+ get contentClass() {
698
+ return CONTENT_CLASSES;
552
699
  }
553
- onBlur() {
554
- this.onTouched();
700
+ get controlClass() {
701
+ return CONTROL_CLASSES;
555
702
  }
556
- open() {
557
- if (this.isDisabled || this.useNative) {
558
- return;
559
- }
560
- this.isOpen = true;
561
- this.syncDisplayMonthWithValue();
703
+ get indicatorContainerClass() {
704
+ return INDICATOR_CONTAINER_CLASSES;
562
705
  }
563
- toggle() {
564
- if (this.isOpen) {
565
- this.isOpen = false;
566
- return;
567
- }
568
- this.open();
706
+ indicatorClass(isActive) {
707
+ return [INDICATOR_BASE_CLASSES, isActive ? INDICATOR_ACTIVE_CLASSES : '']
708
+ .filter(Boolean)
709
+ .join(' ');
569
710
  }
570
- prevMonth() {
571
- this.displayMonth = new Date(this.displayMonth.getFullYear(), this.displayMonth.getMonth() - 1, 1);
572
- this.updateCalendar();
711
+ previous() {
712
+ this.goTo(this.activeIndex - 1);
573
713
  }
574
- nextMonth() {
575
- this.displayMonth = new Date(this.displayMonth.getFullYear(), this.displayMonth.getMonth() + 1, 1);
576
- this.updateCalendar();
714
+ next() {
715
+ this.goTo(this.activeIndex + 1);
577
716
  }
578
- dayButtonClass(day) {
579
- return [
580
- DAY_BUTTON_BASE,
581
- day.inMonth ? '' : DAY_OUTSIDE_MONTH,
582
- day.isToday ? DAY_TODAY : '',
583
- this.isSelected(day.date) ? DAY_SELECTED : DAY_HOVER
584
- ]
585
- .filter(Boolean)
586
- .join(' ');
717
+ goTo(index) {
718
+ const nextIndex = this.normalizeIndex(index);
719
+ this.activeIndex = nextIndex;
720
+ this.activeIndexChange.emit(nextIndex);
587
721
  }
588
- onDocumentClick(event) {
589
- if (this.useNative || !this.isOpen) {
590
- return;
722
+ normalizeIndex(index) {
723
+ if (!this.items.length) {
724
+ return 0;
591
725
  }
592
- const target = event.target;
593
- if (target && !target.closest(`#${this.panelId}`) && !target.closest(`[data-cxs-datepicker="${this.panelId}"]`)) {
594
- this.isOpen = false;
726
+ if (this.loop) {
727
+ const total = this.items.length;
728
+ return (index + total) % total;
595
729
  }
730
+ return this.clampIndex(index);
596
731
  }
597
- onEscape() {
598
- if (this.useNative || !this.isOpen) {
599
- return;
732
+ clampIndex(index) {
733
+ if (!this.items.length) {
734
+ return 0;
600
735
  }
601
- this.isOpen = false;
736
+ return Math.min(Math.max(0, index), this.items.length - 1);
602
737
  }
603
- syncDisplayMonthWithValue() {
604
- const selected = this.parseDate(this.value);
605
- this.displayMonth = selected ?? new Date();
606
- this.updateCalendar();
738
+ resetAutoplay() {
739
+ this.clearAutoplay();
740
+ if (!this.autoplay || this.items.length <= 1) {
741
+ return;
742
+ }
743
+ const intervalMs = Math.max(1000, this.interval);
744
+ this.timerId = setInterval(() => {
745
+ this.next();
746
+ }, intervalMs);
747
+ }
748
+ clearAutoplay() {
749
+ if (this.timerId) {
750
+ clearInterval(this.timerId);
751
+ this.timerId = null;
752
+ }
607
753
  }
608
- updateCalendar() {
609
- const year = this.displayMonth.getFullYear();
754
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsCarouselComponent, deps: [{ token: 'class', attribute: true }], target: i0.ɵɵFactoryTarget.Component });
755
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CxsCarouselComponent, isStandalone: true, selector: "cxs-carousel", inputs: { items: "items", activeIndex: "activeIndex", autoplay: "autoplay", interval: "interval", loop: "loop", showControls: "showControls", showIndicators: "showIndicators", ariaLabel: "ariaLabel" }, outputs: { activeIndexChange: "activeIndexChange" }, usesOnChanges: true, ngImport: i0, template: "<div [class]=\"wrapperClass\" [attr.aria-label]=\"ariaLabel\">\n <div [class]=\"trackClass\" [style.transform]=\"trackStyle\">\n @for (item of items; track item) {\n <div [class]=\"slideClass\">\n @if (item.image) {\n <img\n [class]=\"imageClass\"\n [attr.src]=\"item.image\"\n [attr.alt]=\"item.alt ?? item.title ?? 'Carousel image'\"\n />\n }\n @if (item.title || item.description) {\n <div [class]=\"contentClass\">\n @if (item.title) {\n <div class=\"text-base font-semibold\">{{ item.title }}</div>\n }\n @if (item.description) {\n <div class=\"text-sm text-white/80\">{{ item.description }}</div>\n }\n </div>\n }\n </div>\n }\n </div>\n\n @if (showControls && items.length > 1) {\n <button\n type=\"button\"\n [class]=\"controlClass\"\n style=\"left: 12px\"\n aria-label=\"Previous slide\"\n (click)=\"previous()\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"size-3\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15.75 19.5 8.25 12l7.5-7.5\" />\n </svg>\n </button>\n <button\n type=\"button\"\n [class]=\"controlClass\"\n style=\"right: 12px\"\n aria-label=\"Next slide\"\n (click)=\"next()\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"size-3\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"m8.25 4.5 7.5 7.5-7.5 7.5\" />\n </svg>\n\n </button>\n }\n\n @if (showIndicators && items.length > 1) {\n <div [class]=\"indicatorContainerClass\">\n @for (item of items; track item; let index = $index) {\n <button\n type=\"button\"\n [class]=\"indicatorClass(index === activeIndex)\"\n [attr.aria-label]=\"'Go to slide ' + (index + 1)\"\n (click)=\"goTo(index)\"\n ></button>\n }\n </div>\n }\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
756
+ }
757
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsCarouselComponent, decorators: [{
758
+ type: Component,
759
+ args: [{ selector: 'cxs-carousel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class]=\"wrapperClass\" [attr.aria-label]=\"ariaLabel\">\n <div [class]=\"trackClass\" [style.transform]=\"trackStyle\">\n @for (item of items; track item) {\n <div [class]=\"slideClass\">\n @if (item.image) {\n <img\n [class]=\"imageClass\"\n [attr.src]=\"item.image\"\n [attr.alt]=\"item.alt ?? item.title ?? 'Carousel image'\"\n />\n }\n @if (item.title || item.description) {\n <div [class]=\"contentClass\">\n @if (item.title) {\n <div class=\"text-base font-semibold\">{{ item.title }}</div>\n }\n @if (item.description) {\n <div class=\"text-sm text-white/80\">{{ item.description }}</div>\n }\n </div>\n }\n </div>\n }\n </div>\n\n @if (showControls && items.length > 1) {\n <button\n type=\"button\"\n [class]=\"controlClass\"\n style=\"left: 12px\"\n aria-label=\"Previous slide\"\n (click)=\"previous()\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"size-3\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15.75 19.5 8.25 12l7.5-7.5\" />\n </svg>\n </button>\n <button\n type=\"button\"\n [class]=\"controlClass\"\n style=\"right: 12px\"\n aria-label=\"Next slide\"\n (click)=\"next()\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"size-3\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"m8.25 4.5 7.5 7.5-7.5 7.5\" />\n </svg>\n\n </button>\n }\n\n @if (showIndicators && items.length > 1) {\n <div [class]=\"indicatorContainerClass\">\n @for (item of items; track item; let index = $index) {\n <button\n type=\"button\"\n [class]=\"indicatorClass(index === activeIndex)\"\n [attr.aria-label]=\"'Go to slide ' + (index + 1)\"\n (click)=\"goTo(index)\"\n ></button>\n }\n </div>\n }\n</div>\n" }]
760
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
761
+ type: Attribute,
762
+ args: ['class']
763
+ }] }], propDecorators: { items: [{
764
+ type: Input
765
+ }], activeIndex: [{
766
+ type: Input
767
+ }], autoplay: [{
768
+ type: Input
769
+ }], interval: [{
770
+ type: Input
771
+ }], loop: [{
772
+ type: Input
773
+ }], showControls: [{
774
+ type: Input
775
+ }], showIndicators: [{
776
+ type: Input
777
+ }], ariaLabel: [{
778
+ type: Input
779
+ }], activeIndexChange: [{
780
+ type: Output
781
+ }] } });
782
+
783
+ class CxsDataTableCellDirective {
784
+ template;
785
+ columnKey = '';
786
+ constructor(template) {
787
+ this.template = template;
788
+ }
789
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsDataTableCellDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
790
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.16", type: CxsDataTableCellDirective, isStandalone: true, selector: "[cxsDataTableCell]", inputs: { columnKey: ["cxsDataTableCell", "columnKey"] }, ngImport: i0 });
791
+ }
792
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsDataTableCellDirective, decorators: [{
793
+ type: Directive,
794
+ args: [{
795
+ selector: '[cxsDataTableCell]',
796
+ standalone: true
797
+ }]
798
+ }], ctorParameters: () => [{ type: i0.TemplateRef }], propDecorators: { columnKey: [{
799
+ type: Input,
800
+ args: ['cxsDataTableCell']
801
+ }] } });
802
+
803
+ const WRAPPER_BASE_CLASSES$1 = 'w-full overflow-hidden';
804
+ const WRAPPER_BORDERED_CLASSES$1 = 'rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)]';
805
+ const TABLE_BASE_CLASSES$1 = 'w-full border-collapse text-sm text-[var(--cxs-color-on-surface)]';
806
+ const TABLE_COMPACT_CLASSES$1 = 'text-xs';
807
+ const HEADER_ROW_CLASSES$1 = 'border-b border-[var(--cxs-color-border)] text-xs font-semibold ' +
808
+ 'text-[var(--cxs-color-on-surface-muted)]';
809
+ const HEADER_CELL_BASE_CLASSES$1 = 'px-3 py-2 text-left';
810
+ const HEADER_BUTTON_CLASSES = 'inline-flex items-center gap-2 rounded-[var(--cxs-radius-md)] px-2 py-1 ' +
811
+ 'transition-colors hover:bg-[var(--cxs-color-surface-hover)] ' +
812
+ 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
813
+ 'focus-visible:outline-[var(--cxs-color-focus)]';
814
+ const HEADER_FILTER_INPUT_CLASSES = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
815
+ 'bg-[var(--cxs-color-surface)] px-2 py-1 text-xs text-[var(--cxs-color-on-surface)] ' +
816
+ 'placeholder:text-[var(--cxs-color-on-surface-muted)] focus-visible:outline focus-visible:outline-2 ' +
817
+ 'focus-visible:outline-offset-2 focus-visible:outline-[var(--cxs-color-focus)]';
818
+ const BODY_ROW_BASE_CLASSES$1 = 'border-b border-[var(--cxs-color-border)] last:border-b-0';
819
+ const BODY_ROW_STRIPED_CLASSES$1 = 'odd:bg-[var(--cxs-color-surface)] even:bg-[var(--cxs-color-surface-hover)]';
820
+ const BODY_CELL_BASE_CLASSES$1 = 'px-3 py-2 align-top';
821
+ const BODY_CELL_COMPACT_CLASSES$1 = 'px-2 py-1.5';
822
+ const ALIGN_CLASSES$1 = {
823
+ left: 'text-left',
824
+ center: 'text-center',
825
+ right: 'text-right'
826
+ };
827
+ const EMPTY_ROW_CLASSES$1 = 'text-sm text-[var(--cxs-color-on-surface-muted)]';
828
+ const PAGINATION_CLASSES = 'mt-4 flex flex-wrap items-center justify-between gap-3 text-sm text-[var(--cxs-color-on-surface)]';
829
+ const PAGE_BUTTON_CLASSES = 'inline-flex items-center justify-center rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
830
+ 'px-3 py-1.5 text-sm transition-colors hover:bg-[var(--cxs-color-surface-hover)] ' +
831
+ 'disabled:cursor-not-allowed disabled:opacity-60';
832
+ const PAGE_SELECT_CLASSES = 'rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] bg-[var(--cxs-color-surface)] ' +
833
+ 'px-2 py-1 text-sm';
834
+ const TOOLBAR_CLASSES = 'mb-3 flex flex-wrap items-center justify-between gap-3 text-sm text-[var(--cxs-color-on-surface)]';
835
+ const TOOLBAR_GROUP_CLASSES = 'flex flex-wrap items-center gap-2';
836
+ const TOOLBAR_INPUT_CLASSES = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
837
+ 'bg-[var(--cxs-color-surface)] px-3 py-2 text-sm text-[var(--cxs-color-on-surface)] ' +
838
+ 'placeholder:text-[var(--cxs-color-on-surface-muted)] focus-visible:outline focus-visible:outline-2 ' +
839
+ 'focus-visible:outline-offset-2 focus-visible:outline-[var(--cxs-color-focus)]';
840
+ const TOOLBAR_BUTTON_CLASSES = 'inline-flex items-center justify-center rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
841
+ 'px-3 py-2 text-sm transition-colors hover:bg-[var(--cxs-color-surface-hover)]';
842
+ const BULK_BAR_CLASSES = 'mb-3 flex flex-wrap items-center justify-between gap-3 rounded-[var(--cxs-radius-md)] ' +
843
+ 'border border-[var(--cxs-color-border)] bg-[var(--cxs-color-surface)] px-3 py-2 text-sm';
844
+ const RESIZE_HANDLE_CLASSES = 'absolute right-0 top-0 h-full w-2 cursor-col-resize touch-none';
845
+ class CxsDataTableComponent {
846
+ hostClass;
847
+ columns = [];
848
+ data = [];
849
+ caption;
850
+ ariaLabel = 'Data table';
851
+ striped = false;
852
+ bordered = false;
853
+ compact = false;
854
+ loading = false;
855
+ emptyMessage = 'No data available';
856
+ emptyCell = '—';
857
+ pageSize = 10;
858
+ pageIndex = 1;
859
+ pageSizeOptions = [10, 20, 50];
860
+ showPagination = true;
861
+ showPageSize = true;
862
+ manualPagination = false;
863
+ total;
864
+ sortKey;
865
+ sortDirection = 'asc';
866
+ manualSort = false;
867
+ showToolbar = true;
868
+ showFilters = true;
869
+ showColumnVisibility = true;
870
+ showGlobalSearch = true;
871
+ manualFilter = false;
872
+ selectable = false;
873
+ rowKey = 'id';
874
+ selectedKeys = [];
875
+ bulkActions = [];
876
+ pageChange = new EventEmitter();
877
+ pageSizeChange = new EventEmitter();
878
+ sortChange = new EventEmitter();
879
+ filterChange = new EventEmitter();
880
+ selectionChange = new EventEmitter();
881
+ bulkAction = new EventEmitter();
882
+ cellTemplates;
883
+ currentPage = 1;
884
+ currentPageSize = 10;
885
+ globalFilter = '';
886
+ columnFilters = {};
887
+ columnVisibility = {};
888
+ columnWidths = {};
889
+ isResizing = false;
890
+ resizingColumn;
891
+ pinnedOffsetsLeft = {};
892
+ pinnedOffsetsRight = {};
893
+ filtersOpen = true;
894
+ resizeStartX = 0;
895
+ resizeStartWidth = 0;
896
+ constructor(hostClass) {
897
+ this.hostClass = hostClass;
898
+ }
899
+ ngOnChanges(changes) {
900
+ if (changes['pageIndex']) {
901
+ this.currentPage = Math.max(1, this.pageIndex);
902
+ }
903
+ if (changes['pageSize']) {
904
+ this.currentPageSize = Math.max(1, this.pageSize);
905
+ }
906
+ if (changes['columns']) {
907
+ this.initializeVisibility();
908
+ this.initializeWidths();
909
+ this.updatePinnedOffsets();
910
+ }
911
+ if (changes['showFilters']) {
912
+ this.filtersOpen = this.showFilters;
913
+ }
914
+ if (changes['selectedKeys']) {
915
+ this.selectedKeys = [...this.selectedKeys];
916
+ }
917
+ }
918
+ get wrapperClass() {
919
+ return [
920
+ WRAPPER_BASE_CLASSES$1,
921
+ this.bordered ? WRAPPER_BORDERED_CLASSES$1 : '',
922
+ this.hostClass
923
+ ]
924
+ .filter(Boolean)
925
+ .join(' ');
926
+ }
927
+ get tableClass() {
928
+ return [TABLE_BASE_CLASSES$1, this.compact ? TABLE_COMPACT_CLASSES$1 : '']
929
+ .filter(Boolean)
930
+ .join(' ');
931
+ }
932
+ get headerRowClass() {
933
+ return HEADER_ROW_CLASSES$1;
934
+ }
935
+ get bodyRowClass() {
936
+ return [
937
+ BODY_ROW_BASE_CLASSES$1,
938
+ this.striped ? BODY_ROW_STRIPED_CLASSES$1 : ''
939
+ ]
940
+ .filter(Boolean)
941
+ .join(' ');
942
+ }
943
+ get emptyRowClass() {
944
+ return EMPTY_ROW_CLASSES$1;
945
+ }
946
+ get paginationClass() {
947
+ return PAGINATION_CLASSES;
948
+ }
949
+ get pageButtonClass() {
950
+ return PAGE_BUTTON_CLASSES;
951
+ }
952
+ get pageSelectClass() {
953
+ return PAGE_SELECT_CLASSES;
954
+ }
955
+ get toolbarClass() {
956
+ return TOOLBAR_CLASSES;
957
+ }
958
+ get toolbarGroupClass() {
959
+ return TOOLBAR_GROUP_CLASSES;
960
+ }
961
+ get toolbarInputClass() {
962
+ return TOOLBAR_INPUT_CLASSES;
963
+ }
964
+ get toolbarButtonClass() {
965
+ return TOOLBAR_BUTTON_CLASSES;
966
+ }
967
+ get bulkBarClass() {
968
+ return BULK_BAR_CLASSES;
969
+ }
970
+ get resizeHandleClass() {
971
+ return RESIZE_HANDLE_CLASSES;
972
+ }
973
+ get colSpan() {
974
+ return Math.max(this.visibleColumns.length + (this.selectable ? 1 : 0), 1);
975
+ }
976
+ get totalItems() {
977
+ if (this.manualPagination && this.total !== undefined) {
978
+ return this.total;
979
+ }
980
+ return this.data.length;
981
+ }
982
+ get totalPages() {
983
+ return Math.max(1, Math.ceil(this.totalItems / this.currentPageSize));
984
+ }
985
+ get pageLabel() {
986
+ return `Page ${this.currentPage} of ${this.totalPages}`;
987
+ }
988
+ get displayRows() {
989
+ const filtered = this.manualFilter ? this.data : this.getFilteredData(this.data);
990
+ const sorted = this.manualSort ? filtered : this.getSortedData(filtered);
991
+ if (this.manualPagination) {
992
+ return sorted;
993
+ }
994
+ const start = (this.currentPage - 1) * this.currentPageSize;
995
+ return sorted.slice(start, start + this.currentPageSize);
996
+ }
997
+ get visibleColumns() {
998
+ return this.columns.filter((column) => this.columnVisibility[column.key] !== false);
999
+ }
1000
+ get selectedCount() {
1001
+ return this.selectedKeys.length;
1002
+ }
1003
+ get allVisibleSelected() {
1004
+ if (!this.displayRows.length) {
1005
+ return false;
1006
+ }
1007
+ return this.displayRows.every((row) => this.isSelected(row));
1008
+ }
1009
+ get someVisibleSelected() {
1010
+ return this.displayRows.some((row) => this.isSelected(row)) && !this.allVisibleSelected;
1011
+ }
1012
+ headerCellClass(column) {
1013
+ return [
1014
+ HEADER_CELL_BASE_CLASSES$1,
1015
+ ALIGN_CLASSES$1[column.align ?? 'left'],
1016
+ column.headerClass ?? ''
1017
+ ]
1018
+ .filter(Boolean)
1019
+ .join(' ');
1020
+ }
1021
+ headerButtonClass() {
1022
+ return HEADER_BUTTON_CLASSES;
1023
+ }
1024
+ headerFilterInputClass() {
1025
+ return HEADER_FILTER_INPUT_CLASSES;
1026
+ }
1027
+ bodyCellClass(column) {
1028
+ return [
1029
+ BODY_CELL_BASE_CLASSES$1,
1030
+ this.compact ? BODY_CELL_COMPACT_CLASSES$1 : '',
1031
+ ALIGN_CLASSES$1[column.align ?? 'left'],
1032
+ column.cellClass ?? ''
1033
+ ]
1034
+ .filter(Boolean)
1035
+ .join(' ');
1036
+ }
1037
+ cellTemplate(columnKey) {
1038
+ if (!this.cellTemplates) {
1039
+ return null;
1040
+ }
1041
+ return this.cellTemplates.find((template) => template.columnKey === columnKey)?.template ?? null;
1042
+ }
1043
+ cellValue(row, column) {
1044
+ const value = row[column.key];
1045
+ if (column.formatter) {
1046
+ return column.formatter(value, row);
1047
+ }
1048
+ if (value === null || value === undefined || value === '') {
1049
+ return this.emptyCell;
1050
+ }
1051
+ return String(value);
1052
+ }
1053
+ columnWidth(column) {
1054
+ return this.columnWidths[column.key] ?? column.width ?? null;
1055
+ }
1056
+ columnMinWidth(column) {
1057
+ if (column.minWidth) {
1058
+ return `${column.minWidth}px`;
1059
+ }
1060
+ return null;
1061
+ }
1062
+ pinnedStyle(column) {
1063
+ if (!column.pinned) {
1064
+ return null;
1065
+ }
1066
+ if (column.pinned === 'left') {
1067
+ return { left: `${this.pinnedOffsetsLeft[column.key] ?? 0}px` };
1068
+ }
1069
+ return { right: `${this.pinnedOffsetsRight[column.key] ?? 0}px` };
1070
+ }
1071
+ pinnedLeft(column) {
1072
+ const style = this.pinnedStyle(column);
1073
+ return style ? style['left'] ?? null : null;
1074
+ }
1075
+ pinnedRight(column) {
1076
+ const style = this.pinnedStyle(column);
1077
+ return style ? style['right'] ?? null : null;
1078
+ }
1079
+ pinnedClass(column) {
1080
+ if (!column.pinned) {
1081
+ return '';
1082
+ }
1083
+ return 'sticky z-10';
1084
+ }
1085
+ ariaSort(column) {
1086
+ if (!column.sortable) {
1087
+ return null;
1088
+ }
1089
+ if (this.sortKey !== column.key) {
1090
+ return 'none';
1091
+ }
1092
+ return this.sortDirection === 'asc' ? 'ascending' : 'descending';
1093
+ }
1094
+ onSort(column) {
1095
+ if (!column.sortable) {
1096
+ return;
1097
+ }
1098
+ if (this.sortKey === column.key) {
1099
+ this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
1100
+ }
1101
+ else {
1102
+ this.sortKey = column.key;
1103
+ this.sortDirection = 'asc';
1104
+ }
1105
+ this.sortChange.emit({
1106
+ key: this.sortKey,
1107
+ direction: this.sortDirection
1108
+ });
1109
+ }
1110
+ goToPage(page) {
1111
+ const nextPage = Math.min(Math.max(1, page), this.totalPages);
1112
+ this.currentPage = nextPage;
1113
+ this.pageChange.emit(nextPage);
1114
+ }
1115
+ nextPage() {
1116
+ this.goToPage(this.currentPage + 1);
1117
+ }
1118
+ previousPage() {
1119
+ this.goToPage(this.currentPage - 1);
1120
+ }
1121
+ onPageSizeChange(value) {
1122
+ const nextSize = Number(value);
1123
+ if (!Number.isFinite(nextSize) || nextSize <= 0) {
1124
+ return;
1125
+ }
1126
+ this.currentPageSize = nextSize;
1127
+ this.pageSizeChange.emit(nextSize);
1128
+ this.goToPage(1);
1129
+ }
1130
+ onGlobalFilterChange(value) {
1131
+ this.globalFilter = value;
1132
+ this.emitFilterChange();
1133
+ this.goToPage(1);
1134
+ }
1135
+ onColumnFilterChange(columnKey, value) {
1136
+ this.columnFilters[columnKey] = value;
1137
+ this.emitFilterChange();
1138
+ this.goToPage(1);
1139
+ }
1140
+ columnFilterValue(columnKey) {
1141
+ return this.columnFilters[columnKey] || '';
1142
+ }
1143
+ toggleColumnVisibility(columnKey) {
1144
+ this.columnVisibility[columnKey] = !this.columnVisibility[columnKey];
1145
+ this.updatePinnedOffsets();
1146
+ }
1147
+ toggleSelectAll() {
1148
+ if (this.allVisibleSelected) {
1149
+ this.selectedKeys = this.selectedKeys.filter((key) => !this.displayRows.some((row) => this.rowIdentifier(row) === key));
1150
+ }
1151
+ else {
1152
+ const keysToAdd = this.displayRows
1153
+ .map((row) => this.rowIdentifier(row))
1154
+ .filter((key) => !this.selectedKeys.includes(key));
1155
+ this.selectedKeys = [...this.selectedKeys, ...keysToAdd];
1156
+ }
1157
+ this.selectionChange.emit([...this.selectedKeys]);
1158
+ }
1159
+ toggleRowSelection(row) {
1160
+ const key = this.rowIdentifier(row);
1161
+ if (this.selectedKeys.includes(key)) {
1162
+ this.selectedKeys = this.selectedKeys.filter((id) => id !== key);
1163
+ }
1164
+ else {
1165
+ this.selectedKeys = [...this.selectedKeys, key];
1166
+ }
1167
+ this.selectionChange.emit([...this.selectedKeys]);
1168
+ }
1169
+ isSelected(row) {
1170
+ return this.selectedKeys.includes(this.rowIdentifier(row));
1171
+ }
1172
+ runBulkAction(actionId) {
1173
+ this.bulkAction.emit({
1174
+ id: actionId,
1175
+ selected: this.data.filter((row) => this.selectedKeys.includes(this.rowIdentifier(row)))
1176
+ });
1177
+ }
1178
+ onResizeStart(event, column) {
1179
+ if (column.resizable === false) {
1180
+ return;
1181
+ }
1182
+ event.preventDefault();
1183
+ const header = event.target.closest('th');
1184
+ if (!header) {
1185
+ return;
1186
+ }
1187
+ this.isResizing = true;
1188
+ this.resizingColumn = column.key;
1189
+ this.resizeStartX = event.clientX;
1190
+ this.resizeStartWidth = header.getBoundingClientRect().width;
1191
+ const onMove = (moveEvent) => {
1192
+ if (!this.isResizing || !this.resizingColumn) {
1193
+ return;
1194
+ }
1195
+ const delta = moveEvent.clientX - this.resizeStartX;
1196
+ const minWidth = column.minWidth ?? 80;
1197
+ const nextWidth = Math.max(minWidth, this.resizeStartWidth + delta);
1198
+ this.columnWidths[this.resizingColumn] = `${Math.round(nextWidth)}px`;
1199
+ this.updatePinnedOffsets();
1200
+ };
1201
+ const onUp = () => {
1202
+ this.isResizing = false;
1203
+ this.resizingColumn = undefined;
1204
+ window.removeEventListener('mousemove', onMove);
1205
+ window.removeEventListener('mouseup', onUp);
1206
+ };
1207
+ window.addEventListener('mousemove', onMove);
1208
+ window.addEventListener('mouseup', onUp);
1209
+ }
1210
+ getSortedData(rows) {
1211
+ if (!this.sortKey) {
1212
+ return rows;
1213
+ }
1214
+ const direction = this.sortDirection === 'asc' ? 1 : -1;
1215
+ return [...rows].sort((a, b) => {
1216
+ const valueA = a[this.sortKey ?? ''];
1217
+ const valueB = b[this.sortKey ?? ''];
1218
+ const normalizedA = valueA === null || valueA === undefined ? '' : String(valueA);
1219
+ const normalizedB = valueB === null || valueB === undefined ? '' : String(valueB);
1220
+ return normalizedA.localeCompare(normalizedB, undefined, { numeric: true }) * direction;
1221
+ });
1222
+ }
1223
+ getFilteredData(rows) {
1224
+ const global = this.globalFilter.trim().toLowerCase();
1225
+ const filters = this.columnFilters;
1226
+ return rows.filter((row) => {
1227
+ if (global) {
1228
+ const matchesGlobal = this.visibleColumns.some((column) => {
1229
+ const value = row[column.key];
1230
+ return String(value ?? '').toLowerCase().includes(global);
1231
+ });
1232
+ if (!matchesGlobal) {
1233
+ return false;
1234
+ }
1235
+ }
1236
+ return this.visibleColumns.every((column) => {
1237
+ if (!column.filterable) {
1238
+ return true;
1239
+ }
1240
+ const filterValue = (filters[column.key] ?? '').trim().toLowerCase();
1241
+ if (!filterValue) {
1242
+ return true;
1243
+ }
1244
+ const raw = row[column.key];
1245
+ const normalized = String(raw ?? '').toLowerCase();
1246
+ if (column.filterType === 'select') {
1247
+ return normalized === filterValue;
1248
+ }
1249
+ return normalized.includes(filterValue);
1250
+ });
1251
+ });
1252
+ }
1253
+ emitFilterChange() {
1254
+ this.filterChange.emit({
1255
+ global: this.globalFilter,
1256
+ columns: { ...this.columnFilters }
1257
+ });
1258
+ }
1259
+ initializeVisibility() {
1260
+ this.columnVisibility = this.columns.reduce((acc, column) => {
1261
+ acc[column.key] = column.visible !== false;
1262
+ return acc;
1263
+ }, {});
1264
+ }
1265
+ initializeWidths() {
1266
+ this.columnWidths = this.columns.reduce((acc, column) => {
1267
+ if (column.width) {
1268
+ acc[column.key] = column.width;
1269
+ }
1270
+ return acc;
1271
+ }, {});
1272
+ }
1273
+ updatePinnedOffsets() {
1274
+ const leftOffsets = {};
1275
+ const rightOffsets = {};
1276
+ let left = 0;
1277
+ let right = 0;
1278
+ const columns = this.visibleColumns;
1279
+ columns.forEach((column) => {
1280
+ if (column.pinned === 'left') {
1281
+ leftOffsets[column.key] = left;
1282
+ left += this.getWidthValue(column);
1283
+ }
1284
+ });
1285
+ [...columns].reverse().forEach((column) => {
1286
+ if (column.pinned === 'right') {
1287
+ rightOffsets[column.key] = right;
1288
+ right += this.getWidthValue(column);
1289
+ }
1290
+ });
1291
+ this.pinnedOffsetsLeft = leftOffsets;
1292
+ this.pinnedOffsetsRight = rightOffsets;
1293
+ }
1294
+ getWidthValue(column) {
1295
+ const width = this.columnWidths[column.key] ?? column.width;
1296
+ if (!width) {
1297
+ return 160;
1298
+ }
1299
+ const match = width.match(/(\d+)(px)?/);
1300
+ if (match) {
1301
+ return Number(match[1]);
1302
+ }
1303
+ return 160;
1304
+ }
1305
+ rowIdentifier(row) {
1306
+ const value = row[this.rowKey];
1307
+ if (value === undefined || value === null) {
1308
+ return JSON.stringify(row);
1309
+ }
1310
+ return value;
1311
+ }
1312
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsDataTableComponent, deps: [{ token: 'class', attribute: true }], target: i0.ɵɵFactoryTarget.Component });
1313
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CxsDataTableComponent, isStandalone: true, selector: "cxs-data-table", inputs: { columns: "columns", data: "data", caption: "caption", ariaLabel: "ariaLabel", striped: "striped", bordered: "bordered", compact: "compact", loading: "loading", emptyMessage: "emptyMessage", emptyCell: "emptyCell", pageSize: "pageSize", pageIndex: "pageIndex", pageSizeOptions: "pageSizeOptions", showPagination: "showPagination", showPageSize: "showPageSize", manualPagination: "manualPagination", total: "total", sortKey: "sortKey", sortDirection: "sortDirection", manualSort: "manualSort", showToolbar: "showToolbar", showFilters: "showFilters", showColumnVisibility: "showColumnVisibility", showGlobalSearch: "showGlobalSearch", manualFilter: "manualFilter", selectable: "selectable", rowKey: "rowKey", selectedKeys: "selectedKeys", bulkActions: "bulkActions" }, outputs: { pageChange: "pageChange", pageSizeChange: "pageSizeChange", sortChange: "sortChange", filterChange: "filterChange", selectionChange: "selectionChange", bulkAction: "bulkAction" }, queries: [{ propertyName: "cellTemplates", predicate: CxsDataTableCellDirective }], usesOnChanges: true, ngImport: i0, template: "@if (showToolbar) {\n <div [class]=\"toolbarClass\">\n <div [class]=\"toolbarGroupClass\">\n @if (showGlobalSearch) {\n <input\n type=\"search\"\n [class]=\"toolbarInputClass\"\n placeholder=\"Search\"\n [value]=\"globalFilter\"\n (input)=\"onGlobalFilterChange($any($event.target).value)\"\n />\n }\n </div>\n <div [class]=\"toolbarGroupClass\">\n @if (showFilters) {\n <button type=\"button\" [class]=\"toolbarButtonClass\" (click)=\"filtersOpen = !filtersOpen\">\n Filters\n </button>\n }\n @if (showColumnVisibility) {\n <details class=\"relative\">\n <summary [class]=\"toolbarButtonClass\">Columns</summary>\n <div\n class=\"absolute right-0 mt-2 w-48 rounded-(--cxs-radius-md) border border-(--cxs-color-border) bg-(--cxs-color-surface) p-2 shadow-(--cxs-shadow-sm)\"\n >\n @for (column of columns; track column) {\n <label class=\"flex items-center gap-2 px-2 py-1 text-sm\">\n <input\n type=\"checkbox\"\n [checked]=\"columnVisibility[column.key]\"\n (change)=\"toggleColumnVisibility(column.key)\"\n />\n <span>{{ column.label }}</span>\n </label>\n }\n </div>\n </details>\n }\n </div>\n </div>\n}\n\n@if (selectable && selectedCount > 0 && bulkActions.length > 0) {\n <div [class]=\"bulkBarClass\">\n <span>{{ selectedCount }} selected</span>\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (action of bulkActions; track action) {\n <button type=\"button\" [class]=\"toolbarButtonClass\" (click)=\"runBulkAction(action.id)\">\n {{ action.label }}\n </button>\n }\n </div>\n </div>\n}\n\n<div [class]=\"wrapperClass\">\n <table [class]=\"tableClass\" [attr.aria-label]=\"ariaLabel\">\n @if (caption) {\n <caption class=\"sr-only\">{{ caption }}</caption>\n }\n <thead>\n <tr [class]=\"headerRowClass\">\n @if (selectable) {\n <th class=\"px-3 py-2\">\n <input\n type=\"checkbox\"\n [checked]=\"allVisibleSelected\"\n [indeterminate]=\"someVisibleSelected\"\n (change)=\"toggleSelectAll()\"\n aria-label=\"Select all rows\"\n />\n </th>\n }\n @for (column of visibleColumns; track column) {\n <th\n class=\"relative\"\n [class]=\"headerCellClass(column) + ' ' + pinnedClass(column)\"\n [style.width]=\"columnWidth(column)\"\n [style.minWidth]=\"columnMinWidth(column)\"\n [style.left]=\"pinnedLeft(column)\"\n [style.right]=\"pinnedRight(column)\"\n scope=\"col\"\n [attr.aria-sort]=\"ariaSort(column)\"\n >\n @if (column.sortable) {\n <button\n type=\"button\"\n [class]=\"headerButtonClass()\"\n (click)=\"onSort(column)\"\n >\n <span>{{ column.label }}</span>\n @if (sortKey === column.key) {\n @if (sortDirection === 'asc') {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"1.5\"\n stroke=\"currentColor\"\n class=\"h-4 w-4 text-(--cxs-color-on-surface-muted)\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M4.5 10.5 12 3m0 0 7.5 7.5M12 3v18\"\n />\n </svg>\n } @else {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"1.5\"\n stroke=\"currentColor\"\n class=\"h-4 w-4 text-(--cxs-color-on-surface-muted)\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M19.5 13.5 12 21m0 0-7.5-7.5M12 21V3\"\n />\n </svg>\n }\n }\n </button>\n } @else {\n {{ column.label }}\n }\n @if (column.resizable !== false) {\n <span\n [class]=\"resizeHandleClass\"\n (mousedown)=\"onResizeStart($event, column)\"\n ></span>\n }\n </th>\n }\n </tr>\n @if (showFilters && filtersOpen) {\n <tr [class]=\"headerRowClass\">\n @if (selectable) {\n <th class=\"px-3 py-2\"></th>\n }\n @for (column of visibleColumns; track column) {\n <th\n [class]=\"headerCellClass(column) + ' ' + pinnedClass(column)\"\n [style.left]=\"pinnedLeft(column)\"\n [style.right]=\"pinnedRight(column)\"\n >\n @if (column.filterable) {\n @if (column.filterType === 'select') {\n <select\n [class]=\"headerFilterInputClass()\"\n [value]=\"columnFilterValue(column.key)\"\n (change)=\"onColumnFilterChange(column.key, $any($event.target).value)\"\n >\n <option value=\"\">All</option>\n @for (option of column.filterOptions ?? []; track option) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </select>\n } @else {\n <input\n [class]=\"headerFilterInputClass()\"\n [attr.type]=\"column.filterType === 'number' ? 'number' : 'text'\"\n placeholder=\"Filter\"\n [value]=\"columnFilterValue(column.key)\"\n (input)=\"onColumnFilterChange(column.key, $any($event.target).value)\"\n />\n }\n }\n </th>\n }\n </tr>\n }\n </thead>\n <tbody>\n @if (loading) {\n <tr [class]=\"bodyRowClass\">\n <td [class]=\"bodyCellClass({ key: '', label: '' })\" [attr.colspan]=\"colSpan\">\n <span [class]=\"emptyRowClass\">Loading...</span>\n </td>\n </tr>\n } @else if (displayRows.length === 0) {\n <tr [class]=\"bodyRowClass\">\n <td [class]=\"bodyCellClass({ key: '', label: '' })\" [attr.colspan]=\"colSpan\">\n <span [class]=\"emptyRowClass\">{{ emptyMessage }}</span>\n </td>\n </tr>\n } @else {\n @for (row of displayRows; track row) {\n <tr [class]=\"bodyRowClass\">\n @if (selectable) {\n <td class=\"px-3 py-2\">\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(row)\"\n (change)=\"toggleRowSelection(row)\"\n aria-label=\"Select row\"\n />\n </td>\n }\n @for (column of visibleColumns; track column) {\n <td\n [class]=\"bodyCellClass(column) + ' ' + pinnedClass(column)\"\n [style.left]=\"pinnedLeft(column)\"\n [style.right]=\"pinnedRight(column)\"\n >\n @if (cellTemplate(column.key); as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, value: row[column.key], column }\"\n ></ng-container>\n } @else {\n {{ cellValue(row, column) }}\n }\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n@if (showPagination) {\n <div [class]=\"paginationClass\">\n <div class=\"flex items-center gap-2\">\n <button\n type=\"button\"\n [class]=\"pageButtonClass\"\n [disabled]=\"currentPage <= 1\"\n (click)=\"previousPage()\"\n >\n Previous\n </button>\n <button\n type=\"button\"\n [class]=\"pageButtonClass\"\n [disabled]=\"currentPage >= totalPages\"\n (click)=\"nextPage()\"\n >\n Next\n </button>\n <span class=\"text-(--cxs-color-on-surface-muted)\">{{ pageLabel }}</span>\n </div>\n @if (showPageSize) {\n <label class=\"flex items-center gap-2 text-(--cxs-color-on-surface-muted)\">\n <span>Rows</span>\n <select\n #pageSizeSelect\n [class]=\"pageSelectClass\"\n [value]=\"currentPageSize\"\n (change)=\"onPageSizeChange(pageSizeSelect.value)\"\n >\n @for (size of pageSizeOptions; track size) {\n <option [value]=\"size\">{{ size }}</option>\n }\n </select>\n </label>\n }\n </div>\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1314
+ }
1315
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsDataTableComponent, decorators: [{
1316
+ type: Component,
1317
+ args: [{ selector: 'cxs-data-table', standalone: true, imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (showToolbar) {\n <div [class]=\"toolbarClass\">\n <div [class]=\"toolbarGroupClass\">\n @if (showGlobalSearch) {\n <input\n type=\"search\"\n [class]=\"toolbarInputClass\"\n placeholder=\"Search\"\n [value]=\"globalFilter\"\n (input)=\"onGlobalFilterChange($any($event.target).value)\"\n />\n }\n </div>\n <div [class]=\"toolbarGroupClass\">\n @if (showFilters) {\n <button type=\"button\" [class]=\"toolbarButtonClass\" (click)=\"filtersOpen = !filtersOpen\">\n Filters\n </button>\n }\n @if (showColumnVisibility) {\n <details class=\"relative\">\n <summary [class]=\"toolbarButtonClass\">Columns</summary>\n <div\n class=\"absolute right-0 mt-2 w-48 rounded-(--cxs-radius-md) border border-(--cxs-color-border) bg-(--cxs-color-surface) p-2 shadow-(--cxs-shadow-sm)\"\n >\n @for (column of columns; track column) {\n <label class=\"flex items-center gap-2 px-2 py-1 text-sm\">\n <input\n type=\"checkbox\"\n [checked]=\"columnVisibility[column.key]\"\n (change)=\"toggleColumnVisibility(column.key)\"\n />\n <span>{{ column.label }}</span>\n </label>\n }\n </div>\n </details>\n }\n </div>\n </div>\n}\n\n@if (selectable && selectedCount > 0 && bulkActions.length > 0) {\n <div [class]=\"bulkBarClass\">\n <span>{{ selectedCount }} selected</span>\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (action of bulkActions; track action) {\n <button type=\"button\" [class]=\"toolbarButtonClass\" (click)=\"runBulkAction(action.id)\">\n {{ action.label }}\n </button>\n }\n </div>\n </div>\n}\n\n<div [class]=\"wrapperClass\">\n <table [class]=\"tableClass\" [attr.aria-label]=\"ariaLabel\">\n @if (caption) {\n <caption class=\"sr-only\">{{ caption }}</caption>\n }\n <thead>\n <tr [class]=\"headerRowClass\">\n @if (selectable) {\n <th class=\"px-3 py-2\">\n <input\n type=\"checkbox\"\n [checked]=\"allVisibleSelected\"\n [indeterminate]=\"someVisibleSelected\"\n (change)=\"toggleSelectAll()\"\n aria-label=\"Select all rows\"\n />\n </th>\n }\n @for (column of visibleColumns; track column) {\n <th\n class=\"relative\"\n [class]=\"headerCellClass(column) + ' ' + pinnedClass(column)\"\n [style.width]=\"columnWidth(column)\"\n [style.minWidth]=\"columnMinWidth(column)\"\n [style.left]=\"pinnedLeft(column)\"\n [style.right]=\"pinnedRight(column)\"\n scope=\"col\"\n [attr.aria-sort]=\"ariaSort(column)\"\n >\n @if (column.sortable) {\n <button\n type=\"button\"\n [class]=\"headerButtonClass()\"\n (click)=\"onSort(column)\"\n >\n <span>{{ column.label }}</span>\n @if (sortKey === column.key) {\n @if (sortDirection === 'asc') {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"1.5\"\n stroke=\"currentColor\"\n class=\"h-4 w-4 text-(--cxs-color-on-surface-muted)\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M4.5 10.5 12 3m0 0 7.5 7.5M12 3v18\"\n />\n </svg>\n } @else {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"1.5\"\n stroke=\"currentColor\"\n class=\"h-4 w-4 text-(--cxs-color-on-surface-muted)\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M19.5 13.5 12 21m0 0-7.5-7.5M12 21V3\"\n />\n </svg>\n }\n }\n </button>\n } @else {\n {{ column.label }}\n }\n @if (column.resizable !== false) {\n <span\n [class]=\"resizeHandleClass\"\n (mousedown)=\"onResizeStart($event, column)\"\n ></span>\n }\n </th>\n }\n </tr>\n @if (showFilters && filtersOpen) {\n <tr [class]=\"headerRowClass\">\n @if (selectable) {\n <th class=\"px-3 py-2\"></th>\n }\n @for (column of visibleColumns; track column) {\n <th\n [class]=\"headerCellClass(column) + ' ' + pinnedClass(column)\"\n [style.left]=\"pinnedLeft(column)\"\n [style.right]=\"pinnedRight(column)\"\n >\n @if (column.filterable) {\n @if (column.filterType === 'select') {\n <select\n [class]=\"headerFilterInputClass()\"\n [value]=\"columnFilterValue(column.key)\"\n (change)=\"onColumnFilterChange(column.key, $any($event.target).value)\"\n >\n <option value=\"\">All</option>\n @for (option of column.filterOptions ?? []; track option) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </select>\n } @else {\n <input\n [class]=\"headerFilterInputClass()\"\n [attr.type]=\"column.filterType === 'number' ? 'number' : 'text'\"\n placeholder=\"Filter\"\n [value]=\"columnFilterValue(column.key)\"\n (input)=\"onColumnFilterChange(column.key, $any($event.target).value)\"\n />\n }\n }\n </th>\n }\n </tr>\n }\n </thead>\n <tbody>\n @if (loading) {\n <tr [class]=\"bodyRowClass\">\n <td [class]=\"bodyCellClass({ key: '', label: '' })\" [attr.colspan]=\"colSpan\">\n <span [class]=\"emptyRowClass\">Loading...</span>\n </td>\n </tr>\n } @else if (displayRows.length === 0) {\n <tr [class]=\"bodyRowClass\">\n <td [class]=\"bodyCellClass({ key: '', label: '' })\" [attr.colspan]=\"colSpan\">\n <span [class]=\"emptyRowClass\">{{ emptyMessage }}</span>\n </td>\n </tr>\n } @else {\n @for (row of displayRows; track row) {\n <tr [class]=\"bodyRowClass\">\n @if (selectable) {\n <td class=\"px-3 py-2\">\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(row)\"\n (change)=\"toggleRowSelection(row)\"\n aria-label=\"Select row\"\n />\n </td>\n }\n @for (column of visibleColumns; track column) {\n <td\n [class]=\"bodyCellClass(column) + ' ' + pinnedClass(column)\"\n [style.left]=\"pinnedLeft(column)\"\n [style.right]=\"pinnedRight(column)\"\n >\n @if (cellTemplate(column.key); as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, value: row[column.key], column }\"\n ></ng-container>\n } @else {\n {{ cellValue(row, column) }}\n }\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n@if (showPagination) {\n <div [class]=\"paginationClass\">\n <div class=\"flex items-center gap-2\">\n <button\n type=\"button\"\n [class]=\"pageButtonClass\"\n [disabled]=\"currentPage <= 1\"\n (click)=\"previousPage()\"\n >\n Previous\n </button>\n <button\n type=\"button\"\n [class]=\"pageButtonClass\"\n [disabled]=\"currentPage >= totalPages\"\n (click)=\"nextPage()\"\n >\n Next\n </button>\n <span class=\"text-(--cxs-color-on-surface-muted)\">{{ pageLabel }}</span>\n </div>\n @if (showPageSize) {\n <label class=\"flex items-center gap-2 text-(--cxs-color-on-surface-muted)\">\n <span>Rows</span>\n <select\n #pageSizeSelect\n [class]=\"pageSelectClass\"\n [value]=\"currentPageSize\"\n (change)=\"onPageSizeChange(pageSizeSelect.value)\"\n >\n @for (size of pageSizeOptions; track size) {\n <option [value]=\"size\">{{ size }}</option>\n }\n </select>\n </label>\n }\n </div>\n}\n" }]
1318
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
1319
+ type: Attribute,
1320
+ args: ['class']
1321
+ }] }], propDecorators: { columns: [{
1322
+ type: Input
1323
+ }], data: [{
1324
+ type: Input
1325
+ }], caption: [{
1326
+ type: Input
1327
+ }], ariaLabel: [{
1328
+ type: Input
1329
+ }], striped: [{
1330
+ type: Input
1331
+ }], bordered: [{
1332
+ type: Input
1333
+ }], compact: [{
1334
+ type: Input
1335
+ }], loading: [{
1336
+ type: Input
1337
+ }], emptyMessage: [{
1338
+ type: Input
1339
+ }], emptyCell: [{
1340
+ type: Input
1341
+ }], pageSize: [{
1342
+ type: Input
1343
+ }], pageIndex: [{
1344
+ type: Input
1345
+ }], pageSizeOptions: [{
1346
+ type: Input
1347
+ }], showPagination: [{
1348
+ type: Input
1349
+ }], showPageSize: [{
1350
+ type: Input
1351
+ }], manualPagination: [{
1352
+ type: Input
1353
+ }], total: [{
1354
+ type: Input
1355
+ }], sortKey: [{
1356
+ type: Input
1357
+ }], sortDirection: [{
1358
+ type: Input
1359
+ }], manualSort: [{
1360
+ type: Input
1361
+ }], showToolbar: [{
1362
+ type: Input
1363
+ }], showFilters: [{
1364
+ type: Input
1365
+ }], showColumnVisibility: [{
1366
+ type: Input
1367
+ }], showGlobalSearch: [{
1368
+ type: Input
1369
+ }], manualFilter: [{
1370
+ type: Input
1371
+ }], selectable: [{
1372
+ type: Input
1373
+ }], rowKey: [{
1374
+ type: Input
1375
+ }], selectedKeys: [{
1376
+ type: Input
1377
+ }], bulkActions: [{
1378
+ type: Input
1379
+ }], pageChange: [{
1380
+ type: Output
1381
+ }], pageSizeChange: [{
1382
+ type: Output
1383
+ }], sortChange: [{
1384
+ type: Output
1385
+ }], filterChange: [{
1386
+ type: Output
1387
+ }], selectionChange: [{
1388
+ type: Output
1389
+ }], bulkAction: [{
1390
+ type: Output
1391
+ }], cellTemplates: [{
1392
+ type: ContentChildren,
1393
+ args: [CxsDataTableCellDirective]
1394
+ }] } });
1395
+
1396
+ const OVERLAY_CLASSES = 'fixed inset-0 z-50 flex items-center justify-center bg-[rgba(15,23,42,0.45)] p-4';
1397
+ const PANEL_BASE_CLASSES = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
1398
+ 'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)] shadow-[var(--cxs-shadow-sm)] ' +
1399
+ 'p-6 outline-none';
1400
+ const PANEL_SIZE_CLASSES = {
1401
+ sm: 'max-w-sm',
1402
+ md: 'max-w-lg',
1403
+ lg: 'max-w-2xl'
1404
+ };
1405
+ const TITLE_CLASSES$1 = 'text-lg font-semibold';
1406
+ const DESCRIPTION_CLASSES = 'mt-1 text-sm text-[var(--cxs-color-on-surface-muted)]';
1407
+ const BODY_CLASSES = 'mt-4 text-sm text-[var(--cxs-color-on-surface)]';
1408
+ const ACTIONS_CLASSES$1 = 'mt-6 flex flex-wrap items-center justify-end gap-2 ' +
1409
+ '[&>*]:flex [&>*]:flex-wrap [&>*]:items-center [&>*]:justify-end [&>*]:gap-2';
1410
+ const CLOSE_BUTTON_CLASSES$1 = 'inline-flex h-8 w-8 items-center justify-center rounded-[var(--cxs-radius-md)] ' +
1411
+ 'text-[var(--cxs-color-on-surface)] transition-colors hover:bg-[var(--cxs-color-surface-hover)] ' +
1412
+ 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
1413
+ 'focus-visible:outline-[var(--cxs-color-focus)]';
1414
+ class CxsDialogComponent {
1415
+ hostClass;
1416
+ static nextId = 0;
1417
+ open = false;
1418
+ title;
1419
+ description;
1420
+ ariaLabel = 'Dialog';
1421
+ size = 'md';
1422
+ dismissible = true;
1423
+ closeOnBackdrop = true;
1424
+ closeOnEscape = true;
1425
+ openChange = new EventEmitter();
1426
+ closed = new EventEmitter();
1427
+ dialogPanel;
1428
+ titleId = `cxs-dialog-title-${CxsDialogComponent.nextId++}`;
1429
+ descriptionId = `cxs-dialog-description-${CxsDialogComponent.nextId++}`;
1430
+ constructor(hostClass) {
1431
+ this.hostClass = hostClass;
1432
+ }
1433
+ ngOnChanges(changes) {
1434
+ if (changes['open']?.currentValue) {
1435
+ this.focusPanelSoon();
1436
+ }
1437
+ }
1438
+ ngAfterViewInit() {
1439
+ if (this.open) {
1440
+ this.focusPanelSoon();
1441
+ }
1442
+ }
1443
+ get overlayClass() {
1444
+ return OVERLAY_CLASSES;
1445
+ }
1446
+ get panelClass() {
1447
+ return [PANEL_BASE_CLASSES, PANEL_SIZE_CLASSES[this.size], this.hostClass]
1448
+ .filter(Boolean)
1449
+ .join(' ');
1450
+ }
1451
+ get titleClass() {
1452
+ return TITLE_CLASSES$1;
1453
+ }
1454
+ get descriptionClass() {
1455
+ return DESCRIPTION_CLASSES;
1456
+ }
1457
+ get bodyClass() {
1458
+ return BODY_CLASSES;
1459
+ }
1460
+ get actionsClass() {
1461
+ return ACTIONS_CLASSES$1;
1462
+ }
1463
+ get closeButtonClass() {
1464
+ return CLOSE_BUTTON_CLASSES$1;
1465
+ }
1466
+ get ariaLabelValue() {
1467
+ if (this.title) {
1468
+ return null;
1469
+ }
1470
+ return this.ariaLabel;
1471
+ }
1472
+ requestClose(reason) {
1473
+ this.openChange.emit(false);
1474
+ this.closed.emit(reason);
1475
+ }
1476
+ onBackdropClick(event) {
1477
+ if (!this.closeOnBackdrop || !this.open) {
1478
+ return;
1479
+ }
1480
+ if (event.target === event.currentTarget) {
1481
+ this.requestClose('backdrop');
1482
+ }
1483
+ }
1484
+ onKeydown(event) {
1485
+ if (!this.closeOnEscape || !this.open) {
1486
+ return;
1487
+ }
1488
+ if (event.key === 'Escape') {
1489
+ event.preventDefault();
1490
+ this.requestClose('escape');
1491
+ }
1492
+ }
1493
+ focusPanelSoon() {
1494
+ queueMicrotask(() => {
1495
+ this.dialogPanel?.nativeElement.focus();
1496
+ });
1497
+ }
1498
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsDialogComponent, deps: [{ token: 'class', attribute: true }], target: i0.ɵɵFactoryTarget.Component });
1499
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CxsDialogComponent, isStandalone: true, selector: "cxs-dialog", inputs: { open: "open", title: "title", description: "description", ariaLabel: "ariaLabel", size: "size", dismissible: "dismissible", closeOnBackdrop: "closeOnBackdrop", closeOnEscape: "closeOnEscape" }, outputs: { openChange: "openChange", closed: "closed" }, viewQueries: [{ propertyName: "dialogPanel", first: true, predicate: ["dialogPanel"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (open) {\n <div [class]=\"overlayClass\" (click)=\"onBackdropClick($event)\">\n <div\n #dialogPanel\n [class]=\"panelClass\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"title ? titleId : null\"\n [attr.aria-describedby]=\"description ? descriptionId : null\"\n [attr.aria-label]=\"ariaLabelValue\"\n tabindex=\"-1\"\n (keydown)=\"onKeydown($event)\"\n >\n <div class=\"flex items-start gap-3\">\n <div class=\"flex-1\">\n @if (title) {\n <h2 [id]=\"titleId\" [class]=\"titleClass\">{{ title }}</h2>\n }\n @if (description) {\n <p [id]=\"descriptionId\" [class]=\"descriptionClass\">{{ description }}</p>\n }\n </div>\n @if (dismissible) {\n <button\n type=\"button\"\n [class]=\"closeButtonClass\"\n aria-label=\"Close dialog\"\n (click)=\"requestClose('dismiss')\"\n >\n X\n </button>\n }\n </div>\n\n <div [class]=\"bodyClass\">\n <ng-content></ng-content>\n </div>\n\n <div [class]=\"actionsClass\">\n <ng-content select=\"[cxsDialogActions]\"></ng-content>\n </div>\n </div>\n </div>\n}\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
1500
+ }
1501
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsDialogComponent, decorators: [{
1502
+ type: Component,
1503
+ args: [{ selector: 'cxs-dialog', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (open) {\n <div [class]=\"overlayClass\" (click)=\"onBackdropClick($event)\">\n <div\n #dialogPanel\n [class]=\"panelClass\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"title ? titleId : null\"\n [attr.aria-describedby]=\"description ? descriptionId : null\"\n [attr.aria-label]=\"ariaLabelValue\"\n tabindex=\"-1\"\n (keydown)=\"onKeydown($event)\"\n >\n <div class=\"flex items-start gap-3\">\n <div class=\"flex-1\">\n @if (title) {\n <h2 [id]=\"titleId\" [class]=\"titleClass\">{{ title }}</h2>\n }\n @if (description) {\n <p [id]=\"descriptionId\" [class]=\"descriptionClass\">{{ description }}</p>\n }\n </div>\n @if (dismissible) {\n <button\n type=\"button\"\n [class]=\"closeButtonClass\"\n aria-label=\"Close dialog\"\n (click)=\"requestClose('dismiss')\"\n >\n X\n </button>\n }\n </div>\n\n <div [class]=\"bodyClass\">\n <ng-content></ng-content>\n </div>\n\n <div [class]=\"actionsClass\">\n <ng-content select=\"[cxsDialogActions]\"></ng-content>\n </div>\n </div>\n </div>\n}\n" }]
1504
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
1505
+ type: Attribute,
1506
+ args: ['class']
1507
+ }] }], propDecorators: { open: [{
1508
+ type: Input
1509
+ }], title: [{
1510
+ type: Input
1511
+ }], description: [{
1512
+ type: Input
1513
+ }], ariaLabel: [{
1514
+ type: Input
1515
+ }], size: [{
1516
+ type: Input
1517
+ }], dismissible: [{
1518
+ type: Input
1519
+ }], closeOnBackdrop: [{
1520
+ type: Input
1521
+ }], closeOnEscape: [{
1522
+ type: Input
1523
+ }], openChange: [{
1524
+ type: Output
1525
+ }], closed: [{
1526
+ type: Output
1527
+ }], dialogPanel: [{
1528
+ type: ViewChild,
1529
+ args: ['dialogPanel']
1530
+ }] } });
1531
+
1532
+ const BASE_CLASSES$3 = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
1533
+ 'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)] ' +
1534
+ 'placeholder:text-[var(--cxs-color-on-surface-muted)] transition-colors ' +
1535
+ 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
1536
+ 'focus-visible:outline-[var(--cxs-color-focus)] focus-visible:border-[var(--cxs-color-focus)] ' +
1537
+ 'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-60';
1538
+ const VARIANT_CLASSES$3 = {
1539
+ outline: 'bg-[var(--cxs-color-surface)]',
1540
+ filled: 'bg-[var(--cxs-color-surface-hover)]'
1541
+ };
1542
+ const SIZE_CLASSES$2 = {
1543
+ sm: 'h-8 px-3 text-sm',
1544
+ md: 'h-10 px-3 text-sm',
1545
+ lg: 'h-12 px-4 text-base'
1546
+ };
1547
+ const INVALID_CLASSES$3 = 'border-[var(--cxs-color-danger)] focus-visible:border-[var(--cxs-color-danger)] ' +
1548
+ 'focus-visible:outline-[var(--cxs-color-danger)]';
1549
+ const POPOVER_CLASSES = 'absolute left-0 top-full z-10 mt-2 w-full rounded-[var(--cxs-radius-md)] border ' +
1550
+ 'border-[var(--cxs-color-border)] bg-[var(--cxs-color-surface)] p-3 shadow-[var(--cxs-shadow-sm)]';
1551
+ const NAV_BUTTON_CLASSES = 'inline-flex h-8 w-8 items-center justify-center rounded-[var(--cxs-radius-md)] ' +
1552
+ 'text-[var(--cxs-color-on-surface)] transition-colors hover:bg-[var(--cxs-color-surface-hover)] ' +
1553
+ 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
1554
+ 'focus-visible:outline-[var(--cxs-color-focus)]';
1555
+ const SELECT_CLASSES = 'h-8 rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
1556
+ 'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)] text-xs px-2 ' +
1557
+ 'transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
1558
+ 'focus-visible:outline-[var(--cxs-color-focus)]';
1559
+ const DAY_BUTTON_BASE = 'flex h-8 w-8 items-center justify-center rounded-[var(--cxs-radius-md)] text-xs ' +
1560
+ 'transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
1561
+ 'focus-visible:outline-[var(--cxs-color-focus)] disabled:cursor-not-allowed disabled:opacity-40';
1562
+ const DAY_OUTSIDE_MONTH = 'text-[var(--cxs-color-on-surface-muted)]';
1563
+ const DAY_TODAY = 'border border-[var(--cxs-color-primary)]';
1564
+ const DAY_SELECTED = 'bg-[var(--cxs-color-primary)] text-[var(--cxs-color-on-primary)]';
1565
+ const DAY_HOVER = 'hover:bg-[var(--cxs-color-surface-hover)]';
1566
+ class CxsDatepickerComponent {
1567
+ value = '';
1568
+ variant = 'outline';
1569
+ size = 'md';
1570
+ useNative = true;
1571
+ disabled = false;
1572
+ readonly = false;
1573
+ required = false;
1574
+ invalid = false;
1575
+ autofocus = false;
1576
+ id;
1577
+ name;
1578
+ min;
1579
+ max;
1580
+ placeholder;
1581
+ ariaLabel;
1582
+ ariaDescribedby;
1583
+ valueChange = new EventEmitter();
1584
+ static nextId = 0;
1585
+ fallbackId = `cxs-datepicker-panel-${CxsDatepickerComponent.nextId++}`;
1586
+ disabledFromControl = false;
1587
+ onChange = () => { };
1588
+ onTouched = () => { };
1589
+ isOpen = false;
1590
+ displayMonth = new Date();
1591
+ calendarDays = [];
1592
+ weekDayLabels = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
1593
+ monthOptions = Array.from({ length: 12 }, (_, index) => ({
1594
+ value: index,
1595
+ label: new Date(2020, index, 1).toLocaleString('en-US', { month: 'short' })
1596
+ }));
1597
+ get isDisabled() {
1598
+ return this.disabled || this.disabledFromControl;
1599
+ }
1600
+ get panelId() {
1601
+ return this.id ? `${this.id}-panel` : this.fallbackId;
1602
+ }
1603
+ get inputClass() {
1604
+ return [
1605
+ BASE_CLASSES$3,
1606
+ SIZE_CLASSES$2[this.size],
1607
+ VARIANT_CLASSES$3[this.variant],
1608
+ this.invalid ? INVALID_CLASSES$3 : ''
1609
+ ]
1610
+ .filter(Boolean)
1611
+ .join(' ');
1612
+ }
1613
+ get popoverClass() {
1614
+ return POPOVER_CLASSES;
1615
+ }
1616
+ get navButtonClass() {
1617
+ return NAV_BUTTON_CLASSES;
1618
+ }
1619
+ get selectClass() {
1620
+ return SELECT_CLASSES;
1621
+ }
1622
+ get displayMonthLabel() {
1623
+ return this.displayMonth.toLocaleString('en-US', { month: 'long', year: 'numeric' });
1624
+ }
1625
+ get yearOptions() {
1626
+ const baseYear = this.displayMonth.getFullYear();
1627
+ const minYear = this.parseDate(this.min)?.getFullYear() ?? baseYear - 10;
1628
+ const maxYear = this.parseDate(this.max)?.getFullYear() ?? baseYear + 10;
1629
+ const start = Math.min(minYear, maxYear);
1630
+ const end = Math.max(minYear, maxYear);
1631
+ return Array.from({ length: end - start + 1 }, (_, index) => start + index);
1632
+ }
1633
+ writeValue(value) {
1634
+ this.value = value ?? '';
1635
+ this.syncDisplayMonthWithValue();
1636
+ }
1637
+ registerOnChange(fn) {
1638
+ this.onChange = fn;
1639
+ }
1640
+ registerOnTouched(fn) {
1641
+ this.onTouched = fn;
1642
+ }
1643
+ setDisabledState(isDisabled) {
1644
+ this.disabledFromControl = isDisabled;
1645
+ }
1646
+ onInput(event) {
1647
+ const target = event.target;
1648
+ const nextValue = target.value;
1649
+ this.value = nextValue;
1650
+ this.onChange(nextValue);
1651
+ this.valueChange.emit(nextValue);
1652
+ }
1653
+ onDateSelect(day) {
1654
+ if (day.disabled || this.isDisabled) {
1655
+ return;
1656
+ }
1657
+ const nextValue = this.formatDate(day.date);
1658
+ this.value = nextValue;
1659
+ this.onChange(nextValue);
1660
+ this.valueChange.emit(nextValue);
1661
+ this.isOpen = false;
1662
+ }
1663
+ onBlur() {
1664
+ this.onTouched();
1665
+ }
1666
+ open() {
1667
+ if (this.isDisabled || this.useNative) {
1668
+ return;
1669
+ }
1670
+ this.isOpen = true;
1671
+ this.syncDisplayMonthWithValue();
1672
+ }
1673
+ toggle() {
1674
+ if (this.isOpen) {
1675
+ this.isOpen = false;
1676
+ return;
1677
+ }
1678
+ this.open();
1679
+ }
1680
+ prevMonth() {
1681
+ this.displayMonth = new Date(this.displayMonth.getFullYear(), this.displayMonth.getMonth() - 1, 1);
1682
+ this.updateCalendar();
1683
+ }
1684
+ nextMonth() {
1685
+ this.displayMonth = new Date(this.displayMonth.getFullYear(), this.displayMonth.getMonth() + 1, 1);
1686
+ this.updateCalendar();
1687
+ }
1688
+ onMonthChange(event) {
1689
+ const target = event.target;
1690
+ const nextMonth = Number(target.value);
1691
+ if (Number.isNaN(nextMonth)) {
1692
+ return;
1693
+ }
1694
+ this.displayMonth = new Date(this.displayMonth.getFullYear(), nextMonth, 1);
1695
+ this.updateCalendar();
1696
+ }
1697
+ onYearChange(event) {
1698
+ const target = event.target;
1699
+ const nextYear = Number(target.value);
1700
+ if (Number.isNaN(nextYear)) {
1701
+ return;
1702
+ }
1703
+ this.displayMonth = new Date(nextYear, this.displayMonth.getMonth(), 1);
1704
+ this.updateCalendar();
1705
+ }
1706
+ dayButtonClass(day) {
1707
+ return [
1708
+ DAY_BUTTON_BASE,
1709
+ day.inMonth ? '' : DAY_OUTSIDE_MONTH,
1710
+ day.isToday ? DAY_TODAY : '',
1711
+ this.isSelected(day.date) ? DAY_SELECTED : DAY_HOVER
1712
+ ]
1713
+ .filter(Boolean)
1714
+ .join(' ');
1715
+ }
1716
+ onDocumentClick(event) {
1717
+ if (this.useNative || !this.isOpen) {
1718
+ return;
1719
+ }
1720
+ const target = event.target;
1721
+ if (target && !target.closest(`#${this.panelId}`) && !target.closest(`[data-cxs-datepicker="${this.panelId}"]`)) {
1722
+ this.isOpen = false;
1723
+ }
1724
+ }
1725
+ onEscape() {
1726
+ if (this.useNative || !this.isOpen) {
1727
+ return;
1728
+ }
1729
+ this.isOpen = false;
1730
+ }
1731
+ syncDisplayMonthWithValue() {
1732
+ const selected = this.parseDate(this.value);
1733
+ this.displayMonth = selected ?? new Date();
1734
+ this.updateCalendar();
1735
+ }
1736
+ updateCalendar() {
1737
+ const year = this.displayMonth.getFullYear();
610
1738
  const month = this.displayMonth.getMonth();
611
1739
  const startOfMonth = new Date(year, month, 1);
612
1740
  const startDay = startOfMonth.getDay();
@@ -663,7 +1791,7 @@ class CxsDatepickerComponent {
663
1791
  useExisting: forwardRef(() => CxsDatepickerComponent),
664
1792
  multi: true
665
1793
  }
666
- ], ngImport: i0, template: "@if (useNative) {\n <input\n type=\"date\"\n [class]=\"inputClass\"\n [attr.id]=\"id\"\n [attr.name]=\"name\"\n [attr.min]=\"min\"\n [attr.max]=\"max\"\n [attr.placeholder]=\"placeholder\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-describedby]=\"ariaDescribedby\"\n [attr.autofocus]=\"autofocus ? true : null\"\n [value]=\"value\"\n [disabled]=\"isDisabled\"\n [readonly]=\"readonly\"\n [required]=\"required\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n />\n} @else {\n <div class=\"relative w-full\">\n <input\n type=\"text\"\n [class]=\"inputClass\"\n [attr.id]=\"id\"\n [attr.name]=\"name\"\n [attr.placeholder]=\"placeholder\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-describedby]=\"ariaDescribedby\"\n [attr.aria-expanded]=\"isOpen ? 'true' : 'false'\"\n [attr.aria-controls]=\"panelId\"\n [attr.autofocus]=\"autofocus ? true : null\"\n [attr.data-cxs-datepicker]=\"panelId\"\n [value]=\"value\"\n [disabled]=\"isDisabled\"\n [readonly]=\"true\"\n [required]=\"required\"\n (click)=\"open()\"\n (focus)=\"open()\"\n (blur)=\"onBlur()\"\n />\n @if (isOpen) {\n <div [class]=\"popoverClass\" [attr.id]=\"panelId\" role=\"dialog\" aria-label=\"Choose date\">\n <div class=\"flex items-center justify-between\">\n <button type=\"button\" [class]=\"navButtonClass\" aria-label=\"Previous month\" (click)=\"prevMonth()\">\n <\n </button>\n <div class=\"text-sm font-semibold text-[var(--cxs-color-on-surface)]\">\n {{ displayMonthLabel }}\n </div>\n <button type=\"button\" [class]=\"navButtonClass\" aria-label=\"Next month\" (click)=\"nextMonth()\">\n >\n </button>\n </div>\n\n <div class=\"mt-3 grid grid-cols-7 gap-1 text-center text-xs text-[var(--cxs-color-on-surface-muted)]\">\n @for (label of weekDayLabels; track label) {\n <div>{{ label }}</div>\n }\n </div>\n\n <div class=\"mt-2 grid grid-cols-7 gap-1\" role=\"grid\">\n @for (day of calendarDays; track day.key) {\n <button\n type=\"button\"\n [class]=\"dayButtonClass(day)\"\n [disabled]=\"day.disabled\"\n [attr.data-date]=\"day.key\"\n [attr.aria-selected]=\"day.key === value ? 'true' : 'false'\"\n [attr.aria-current]=\"day.isToday ? 'date' : null\"\n (click)=\"onDateSelect(day)\"\n >\n {{ day.label }}\n </button>\n }\n </div>\n </div>\n }\n </div>\n}\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
1794
+ ], ngImport: i0, template: "@if (useNative) {\r\n <input\r\n type=\"date\"\r\n [class]=\"inputClass\"\r\n [attr.id]=\"id\"\r\n [attr.name]=\"name\"\r\n [attr.min]=\"min\"\r\n [attr.max]=\"max\"\r\n [attr.placeholder]=\"placeholder\"\r\n [attr.aria-label]=\"ariaLabel\"\r\n [attr.aria-describedby]=\"ariaDescribedby\"\r\n [attr.autofocus]=\"autofocus ? true : null\"\r\n [value]=\"value\"\r\n [disabled]=\"isDisabled\"\r\n [readonly]=\"readonly\"\r\n [required]=\"required\"\r\n (input)=\"onInput($event)\"\r\n (blur)=\"onBlur()\"\r\n />\r\n} @else {\r\n <div class=\"relative w-full\">\r\n <input\r\n type=\"text\"\r\n [class]=\"inputClass\"\r\n [attr.id]=\"id\"\r\n [attr.name]=\"name\"\r\n [attr.placeholder]=\"placeholder\"\r\n [attr.aria-label]=\"ariaLabel\"\r\n [attr.aria-describedby]=\"ariaDescribedby\"\r\n [attr.aria-expanded]=\"isOpen ? 'true' : 'false'\"\r\n [attr.aria-controls]=\"panelId\"\r\n [attr.autofocus]=\"autofocus ? true : null\"\r\n [attr.data-cxs-datepicker]=\"panelId\"\r\n [value]=\"value\"\r\n [disabled]=\"isDisabled\"\r\n [readonly]=\"true\"\r\n [required]=\"required\"\r\n (click)=\"open()\"\r\n (focus)=\"open()\"\r\n (blur)=\"onBlur()\"\r\n />\r\n @if (isOpen) {\r\n <div [class]=\"popoverClass\" [attr.id]=\"panelId\" role=\"dialog\" aria-label=\"Choose date\">\r\n <div class=\"flex items-center justify-between\">\r\n <button type=\"button\" [class]=\"navButtonClass\" aria-label=\"Previous month\" (click)=\"prevMonth()\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"size-3\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15.75 19.5 8.25 12l7.5-7.5\" />\r\n </svg>\r\n </button>\r\n <div class=\"flex items-center gap-2\">\r\n <select\r\n [class]=\"selectClass\"\r\n [disabled]=\"isDisabled\"\r\n [value]=\"displayMonth.getMonth()\"\r\n aria-label=\"Month\"\r\n (change)=\"onMonthChange($event)\"\r\n >\r\n @for (month of monthOptions; track month.value) {\r\n <option [value]=\"month.value\">{{ month.label }}</option>\r\n }\r\n </select>\r\n <select\r\n [class]=\"selectClass\"\r\n [disabled]=\"isDisabled\"\r\n [value]=\"displayMonth.getFullYear()\"\r\n aria-label=\"Year\"\r\n (change)=\"onYearChange($event)\"\r\n >\r\n @for (year of yearOptions; track year) {\r\n <option [value]=\"year\">{{ year }}</option>\r\n }\r\n </select>\r\n </div>\r\n <button type=\"button\" [class]=\"navButtonClass\" aria-label=\"Next month\" (click)=\"nextMonth()\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"size-3\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"m8.25 4.5 7.5 7.5-7.5 7.5\" />\r\n </svg>\r\n </button>\r\n </div>\r\n\r\n <div class=\"mt-3 grid grid-cols-7 gap-1 text-center text-xs text-(--cxs-color-on-surface-muted)\">\r\n @for (label of weekDayLabels; track label) {\r\n <div>{{ label }}</div>\r\n }\r\n </div>\r\n\r\n <div class=\"mt-2 grid grid-cols-7 gap-1\" role=\"grid\">\r\n @for (day of calendarDays; track day.key) {\r\n <button\r\n type=\"button\"\r\n [class]=\"dayButtonClass(day)\"\r\n [disabled]=\"day.disabled\"\r\n [attr.data-date]=\"day.key\"\r\n [attr.aria-selected]=\"day.key === value ? 'true' : 'false'\"\r\n [attr.aria-current]=\"day.isToday ? 'date' : null\"\r\n (click)=\"onDateSelect(day)\"\r\n >\r\n {{ day.label }}\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n}\r\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
667
1795
  }
668
1796
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsDatepickerComponent, decorators: [{
669
1797
  type: Component,
@@ -673,7 +1801,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
673
1801
  useExisting: forwardRef(() => CxsDatepickerComponent),
674
1802
  multi: true
675
1803
  }
676
- ], template: "@if (useNative) {\n <input\n type=\"date\"\n [class]=\"inputClass\"\n [attr.id]=\"id\"\n [attr.name]=\"name\"\n [attr.min]=\"min\"\n [attr.max]=\"max\"\n [attr.placeholder]=\"placeholder\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-describedby]=\"ariaDescribedby\"\n [attr.autofocus]=\"autofocus ? true : null\"\n [value]=\"value\"\n [disabled]=\"isDisabled\"\n [readonly]=\"readonly\"\n [required]=\"required\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n />\n} @else {\n <div class=\"relative w-full\">\n <input\n type=\"text\"\n [class]=\"inputClass\"\n [attr.id]=\"id\"\n [attr.name]=\"name\"\n [attr.placeholder]=\"placeholder\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-describedby]=\"ariaDescribedby\"\n [attr.aria-expanded]=\"isOpen ? 'true' : 'false'\"\n [attr.aria-controls]=\"panelId\"\n [attr.autofocus]=\"autofocus ? true : null\"\n [attr.data-cxs-datepicker]=\"panelId\"\n [value]=\"value\"\n [disabled]=\"isDisabled\"\n [readonly]=\"true\"\n [required]=\"required\"\n (click)=\"open()\"\n (focus)=\"open()\"\n (blur)=\"onBlur()\"\n />\n @if (isOpen) {\n <div [class]=\"popoverClass\" [attr.id]=\"panelId\" role=\"dialog\" aria-label=\"Choose date\">\n <div class=\"flex items-center justify-between\">\n <button type=\"button\" [class]=\"navButtonClass\" aria-label=\"Previous month\" (click)=\"prevMonth()\">\n <\n </button>\n <div class=\"text-sm font-semibold text-[var(--cxs-color-on-surface)]\">\n {{ displayMonthLabel }}\n </div>\n <button type=\"button\" [class]=\"navButtonClass\" aria-label=\"Next month\" (click)=\"nextMonth()\">\n >\n </button>\n </div>\n\n <div class=\"mt-3 grid grid-cols-7 gap-1 text-center text-xs text-[var(--cxs-color-on-surface-muted)]\">\n @for (label of weekDayLabels; track label) {\n <div>{{ label }}</div>\n }\n </div>\n\n <div class=\"mt-2 grid grid-cols-7 gap-1\" role=\"grid\">\n @for (day of calendarDays; track day.key) {\n <button\n type=\"button\"\n [class]=\"dayButtonClass(day)\"\n [disabled]=\"day.disabled\"\n [attr.data-date]=\"day.key\"\n [attr.aria-selected]=\"day.key === value ? 'true' : 'false'\"\n [attr.aria-current]=\"day.isToday ? 'date' : null\"\n (click)=\"onDateSelect(day)\"\n >\n {{ day.label }}\n </button>\n }\n </div>\n </div>\n }\n </div>\n}\n" }]
1804
+ ], template: "@if (useNative) {\r\n <input\r\n type=\"date\"\r\n [class]=\"inputClass\"\r\n [attr.id]=\"id\"\r\n [attr.name]=\"name\"\r\n [attr.min]=\"min\"\r\n [attr.max]=\"max\"\r\n [attr.placeholder]=\"placeholder\"\r\n [attr.aria-label]=\"ariaLabel\"\r\n [attr.aria-describedby]=\"ariaDescribedby\"\r\n [attr.autofocus]=\"autofocus ? true : null\"\r\n [value]=\"value\"\r\n [disabled]=\"isDisabled\"\r\n [readonly]=\"readonly\"\r\n [required]=\"required\"\r\n (input)=\"onInput($event)\"\r\n (blur)=\"onBlur()\"\r\n />\r\n} @else {\r\n <div class=\"relative w-full\">\r\n <input\r\n type=\"text\"\r\n [class]=\"inputClass\"\r\n [attr.id]=\"id\"\r\n [attr.name]=\"name\"\r\n [attr.placeholder]=\"placeholder\"\r\n [attr.aria-label]=\"ariaLabel\"\r\n [attr.aria-describedby]=\"ariaDescribedby\"\r\n [attr.aria-expanded]=\"isOpen ? 'true' : 'false'\"\r\n [attr.aria-controls]=\"panelId\"\r\n [attr.autofocus]=\"autofocus ? true : null\"\r\n [attr.data-cxs-datepicker]=\"panelId\"\r\n [value]=\"value\"\r\n [disabled]=\"isDisabled\"\r\n [readonly]=\"true\"\r\n [required]=\"required\"\r\n (click)=\"open()\"\r\n (focus)=\"open()\"\r\n (blur)=\"onBlur()\"\r\n />\r\n @if (isOpen) {\r\n <div [class]=\"popoverClass\" [attr.id]=\"panelId\" role=\"dialog\" aria-label=\"Choose date\">\r\n <div class=\"flex items-center justify-between\">\r\n <button type=\"button\" [class]=\"navButtonClass\" aria-label=\"Previous month\" (click)=\"prevMonth()\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"size-3\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15.75 19.5 8.25 12l7.5-7.5\" />\r\n </svg>\r\n </button>\r\n <div class=\"flex items-center gap-2\">\r\n <select\r\n [class]=\"selectClass\"\r\n [disabled]=\"isDisabled\"\r\n [value]=\"displayMonth.getMonth()\"\r\n aria-label=\"Month\"\r\n (change)=\"onMonthChange($event)\"\r\n >\r\n @for (month of monthOptions; track month.value) {\r\n <option [value]=\"month.value\">{{ month.label }}</option>\r\n }\r\n </select>\r\n <select\r\n [class]=\"selectClass\"\r\n [disabled]=\"isDisabled\"\r\n [value]=\"displayMonth.getFullYear()\"\r\n aria-label=\"Year\"\r\n (change)=\"onYearChange($event)\"\r\n >\r\n @for (year of yearOptions; track year) {\r\n <option [value]=\"year\">{{ year }}</option>\r\n }\r\n </select>\r\n </div>\r\n <button type=\"button\" [class]=\"navButtonClass\" aria-label=\"Next month\" (click)=\"nextMonth()\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"size-3\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"m8.25 4.5 7.5 7.5-7.5 7.5\" />\r\n </svg>\r\n </button>\r\n </div>\r\n\r\n <div class=\"mt-3 grid grid-cols-7 gap-1 text-center text-xs text-(--cxs-color-on-surface-muted)\">\r\n @for (label of weekDayLabels; track label) {\r\n <div>{{ label }}</div>\r\n }\r\n </div>\r\n\r\n <div class=\"mt-2 grid grid-cols-7 gap-1\" role=\"grid\">\r\n @for (day of calendarDays; track day.key) {\r\n <button\r\n type=\"button\"\r\n [class]=\"dayButtonClass(day)\"\r\n [disabled]=\"day.disabled\"\r\n [attr.data-date]=\"day.key\"\r\n [attr.aria-selected]=\"day.key === value ? 'true' : 'false'\"\r\n [attr.aria-current]=\"day.isToday ? 'date' : null\"\r\n (click)=\"onDateSelect(day)\"\r\n >\r\n {{ day.label }}\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n}\r\n" }]
677
1805
  }], propDecorators: { value: [{
678
1806
  type: Input
679
1807
  }], variant: [{
@@ -716,13 +1844,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
716
1844
  args: ['document:keydown.escape']
717
1845
  }] } });
718
1846
 
719
- const BASE_CLASSES$1 = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
1847
+ const BASE_CLASSES$2 = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
720
1848
  'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)] ' +
721
1849
  'placeholder:text-[var(--cxs-color-on-surface-muted)] transition-colors ' +
722
1850
  'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
723
1851
  'focus-visible:outline-[var(--cxs-color-focus)] focus-visible:border-[var(--cxs-color-focus)] ' +
724
1852
  'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-60';
725
- const VARIANT_CLASSES$1 = {
1853
+ const VARIANT_CLASSES$2 = {
726
1854
  outline: 'bg-[var(--cxs-color-surface)]',
727
1855
  filled: 'bg-[var(--cxs-color-surface-hover)]'
728
1856
  };
@@ -760,9 +1888,9 @@ class CxsInputComponent {
760
1888
  }
761
1889
  get inputClass() {
762
1890
  return [
763
- BASE_CLASSES$1,
1891
+ BASE_CLASSES$2,
764
1892
  SIZE_CLASSES$1[this.size],
765
- VARIANT_CLASSES$1[this.variant],
1893
+ VARIANT_CLASSES$2[this.variant],
766
1894
  this.invalid ? INVALID_CLASSES$2 : ''
767
1895
  ]
768
1896
  .filter(Boolean)
@@ -787,63 +1915,204 @@ class CxsInputComponent {
787
1915
  this.onChange(nextValue);
788
1916
  this.valueChange.emit(nextValue);
789
1917
  }
790
- onBlur() {
791
- this.onTouched();
1918
+ onBlur() {
1919
+ this.onTouched();
1920
+ }
1921
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1922
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: CxsInputComponent, isStandalone: true, selector: "cxs-input", inputs: { value: "value", type: "type", variant: "variant", size: "size", disabled: "disabled", readonly: "readonly", required: "required", invalid: "invalid", autofocus: "autofocus", id: "id", name: "name", placeholder: "placeholder", autocomplete: "autocomplete", inputmode: "inputmode", spellcheck: "spellcheck", ariaLabel: "ariaLabel", ariaDescribedby: "ariaDescribedby" }, outputs: { valueChange: "valueChange" }, providers: [
1923
+ {
1924
+ provide: NG_VALUE_ACCESSOR,
1925
+ useExisting: forwardRef(() => CxsInputComponent),
1926
+ multi: true
1927
+ }
1928
+ ], ngImport: i0, template: "<input\r\n [class]=\"inputClass\"\r\n [attr.id]=\"id\"\r\n [attr.name]=\"name\"\r\n [attr.type]=\"type\"\r\n [attr.placeholder]=\"placeholder\"\r\n [attr.autocomplete]=\"autocomplete\"\r\n [attr.inputmode]=\"inputmode\"\r\n [attr.spellcheck]=\"spellcheck === null ? null : spellcheck\"\r\n [attr.aria-label]=\"ariaLabel\"\r\n [attr.aria-describedby]=\"ariaDescribedby\"\r\n [attr.aria-invalid]=\"invalid ? 'true' : null\"\r\n [attr.autofocus]=\"autofocus ? true : null\"\r\n [disabled]=\"isDisabled\"\r\n [readonly]=\"readonly\"\r\n [required]=\"required\"\r\n [value]=\"value\"\r\n (input)=\"onInput($event)\"\r\n (blur)=\"onBlur()\"\r\n/>\r\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
1929
+ }
1930
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsInputComponent, decorators: [{
1931
+ type: Component,
1932
+ args: [{ selector: 'cxs-input', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
1933
+ {
1934
+ provide: NG_VALUE_ACCESSOR,
1935
+ useExisting: forwardRef(() => CxsInputComponent),
1936
+ multi: true
1937
+ }
1938
+ ], template: "<input\r\n [class]=\"inputClass\"\r\n [attr.id]=\"id\"\r\n [attr.name]=\"name\"\r\n [attr.type]=\"type\"\r\n [attr.placeholder]=\"placeholder\"\r\n [attr.autocomplete]=\"autocomplete\"\r\n [attr.inputmode]=\"inputmode\"\r\n [attr.spellcheck]=\"spellcheck === null ? null : spellcheck\"\r\n [attr.aria-label]=\"ariaLabel\"\r\n [attr.aria-describedby]=\"ariaDescribedby\"\r\n [attr.aria-invalid]=\"invalid ? 'true' : null\"\r\n [attr.autofocus]=\"autofocus ? true : null\"\r\n [disabled]=\"isDisabled\"\r\n [readonly]=\"readonly\"\r\n [required]=\"required\"\r\n [value]=\"value\"\r\n (input)=\"onInput($event)\"\r\n (blur)=\"onBlur()\"\r\n/>\r\n" }]
1939
+ }], propDecorators: { value: [{
1940
+ type: Input
1941
+ }], type: [{
1942
+ type: Input
1943
+ }], variant: [{
1944
+ type: Input
1945
+ }], size: [{
1946
+ type: Input
1947
+ }], disabled: [{
1948
+ type: Input
1949
+ }], readonly: [{
1950
+ type: Input
1951
+ }], required: [{
1952
+ type: Input
1953
+ }], invalid: [{
1954
+ type: Input
1955
+ }], autofocus: [{
1956
+ type: Input
1957
+ }], id: [{
1958
+ type: Input
1959
+ }], name: [{
1960
+ type: Input
1961
+ }], placeholder: [{
1962
+ type: Input
1963
+ }], autocomplete: [{
1964
+ type: Input
1965
+ }], inputmode: [{
1966
+ type: Input
1967
+ }], spellcheck: [{
1968
+ type: Input
1969
+ }], ariaLabel: [{
1970
+ type: Input
1971
+ }], ariaDescribedby: [{
1972
+ type: Input
1973
+ }], valueChange: [{
1974
+ type: Output
1975
+ }] } });
1976
+
1977
+ const BAR_BASE_CLASSES = 'm-0 flex list-none flex-wrap items-center gap-1 rounded-[var(--cxs-radius-md)] border border-transparent p-1';
1978
+ const BAR_SIZE_CLASSES = {
1979
+ sm: 'text-xs',
1980
+ md: 'text-sm',
1981
+ lg: 'text-base'
1982
+ };
1983
+ const BAR_VARIANT_CLASSES = {
1984
+ default: 'bg-transparent',
1985
+ surface: 'bg-[var(--cxs-color-surface)] border-[var(--cxs-color-border)] shadow-[var(--cxs-shadow-sm)]'
1986
+ };
1987
+ const ITEM_BASE_CLASSES = 'inline-flex items-center gap-2 rounded-[var(--cxs-radius-md)] px-3 py-1.5 font-medium ' +
1988
+ 'transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
1989
+ 'focus-visible:outline-[var(--cxs-color-focus)]';
1990
+ const ITEM_SIZE_CLASSES = {
1991
+ sm: 'px-2.5 py-1 text-xs',
1992
+ md: 'px-3 py-1.5 text-sm',
1993
+ lg: 'px-4 py-2 text-base'
1994
+ };
1995
+ const ITEM_ACTIVE_CLASSES = 'bg-[var(--cxs-color-primary-ghost)] text-[var(--cxs-color-primary)]';
1996
+ const ITEM_INACTIVE_CLASSES = 'text-[var(--cxs-color-on-surface)] hover:bg-[var(--cxs-color-surface-hover)]';
1997
+ const ITEM_DISABLED_CLASSES = 'text-[var(--cxs-color-on-surface-muted)] opacity-60 pointer-events-none';
1998
+ class CxsMenubarComponent {
1999
+ hostClass;
2000
+ items = [];
2001
+ ariaLabel = 'Menubar';
2002
+ size = 'md';
2003
+ variant = 'default';
2004
+ disabled = false;
2005
+ itemSelected = new EventEmitter();
2006
+ menuItems;
2007
+ constructor(hostClass) {
2008
+ this.hostClass = hostClass;
2009
+ }
2010
+ get menubarClass() {
2011
+ return [
2012
+ BAR_BASE_CLASSES,
2013
+ BAR_SIZE_CLASSES[this.size],
2014
+ BAR_VARIANT_CLASSES[this.variant],
2015
+ this.hostClass
2016
+ ]
2017
+ .filter(Boolean)
2018
+ .join(' ');
2019
+ }
2020
+ isItemDisabled(item) {
2021
+ return this.disabled || !!item.disabled;
2022
+ }
2023
+ getItemClass(item) {
2024
+ return [
2025
+ ITEM_BASE_CLASSES,
2026
+ ITEM_SIZE_CLASSES[this.size],
2027
+ item.active ? ITEM_ACTIVE_CLASSES : ITEM_INACTIVE_CLASSES,
2028
+ this.isItemDisabled(item) ? ITEM_DISABLED_CLASSES : ''
2029
+ ]
2030
+ .filter(Boolean)
2031
+ .join(' ');
2032
+ }
2033
+ getItemHref(item) {
2034
+ if (item.href && !this.isItemDisabled(item)) {
2035
+ return item.href;
2036
+ }
2037
+ return null;
2038
+ }
2039
+ getItemRel(item) {
2040
+ if (item.target === '_blank') {
2041
+ return item.rel ?? 'noopener noreferrer';
2042
+ }
2043
+ return item.rel ?? null;
2044
+ }
2045
+ onItemClick(item, event) {
2046
+ if (this.isItemDisabled(item)) {
2047
+ event.preventDefault();
2048
+ event.stopPropagation();
2049
+ return;
2050
+ }
2051
+ this.itemSelected.emit(item);
2052
+ }
2053
+ onKeydown(event) {
2054
+ if (this.disabled) {
2055
+ return;
2056
+ }
2057
+ if (typeof document === 'undefined') {
2058
+ return;
2059
+ }
2060
+ const keys = ['ArrowRight', 'ArrowLeft', 'Home', 'End'];
2061
+ if (!keys.includes(event.key)) {
2062
+ return;
2063
+ }
2064
+ const items = this.getFocusableItems();
2065
+ if (!items.length) {
2066
+ return;
2067
+ }
2068
+ const currentIndex = items.findIndex((item) => item === document.activeElement);
2069
+ let nextIndex = 0;
2070
+ switch (event.key) {
2071
+ case 'Home':
2072
+ nextIndex = 0;
2073
+ break;
2074
+ case 'End':
2075
+ nextIndex = items.length - 1;
2076
+ break;
2077
+ case 'ArrowRight':
2078
+ nextIndex = currentIndex >= 0 ? (currentIndex + 1) % items.length : 0;
2079
+ break;
2080
+ case 'ArrowLeft':
2081
+ nextIndex = currentIndex >= 0 ? (currentIndex - 1 + items.length) % items.length : items.length - 1;
2082
+ break;
2083
+ }
2084
+ items[nextIndex]?.focus();
2085
+ event.preventDefault();
2086
+ }
2087
+ getFocusableItems() {
2088
+ return (this.menuItems?.toArray() ?? [])
2089
+ .map((item) => item.nativeElement)
2090
+ .filter((item) => !item.hasAttribute('data-disabled'));
792
2091
  }
793
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
794
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: CxsInputComponent, isStandalone: true, selector: "cxs-input", inputs: { value: "value", type: "type", variant: "variant", size: "size", disabled: "disabled", readonly: "readonly", required: "required", invalid: "invalid", autofocus: "autofocus", id: "id", name: "name", placeholder: "placeholder", autocomplete: "autocomplete", inputmode: "inputmode", spellcheck: "spellcheck", ariaLabel: "ariaLabel", ariaDescribedby: "ariaDescribedby" }, outputs: { valueChange: "valueChange" }, providers: [
795
- {
796
- provide: NG_VALUE_ACCESSOR,
797
- useExisting: forwardRef(() => CxsInputComponent),
798
- multi: true
799
- }
800
- ], ngImport: i0, template: "<input\n [class]=\"inputClass\"\n [attr.id]=\"id\"\n [attr.name]=\"name\"\n [attr.type]=\"type\"\n [attr.placeholder]=\"placeholder\"\n [attr.autocomplete]=\"autocomplete\"\n [attr.inputmode]=\"inputmode\"\n [attr.spellcheck]=\"spellcheck === null ? null : spellcheck\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-describedby]=\"ariaDescribedby\"\n [attr.aria-invalid]=\"invalid ? 'true' : null\"\n [attr.autofocus]=\"autofocus ? true : null\"\n [disabled]=\"isDisabled\"\n [readonly]=\"readonly\"\n [required]=\"required\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n/>\r\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
2092
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsMenubarComponent, deps: [{ token: 'class', attribute: true }], target: i0.ɵɵFactoryTarget.Component });
2093
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CxsMenubarComponent, isStandalone: true, selector: "cxs-menubar", inputs: { items: "items", ariaLabel: "ariaLabel", size: "size", variant: "variant", disabled: "disabled" }, outputs: { itemSelected: "itemSelected" }, viewQueries: [{ propertyName: "menuItems", predicate: ["menuItem"], descendants: true, read: ElementRef }], ngImport: i0, template: "<nav [attr.aria-label]=\"ariaLabel\">\n <ul [class]=\"menubarClass\" role=\"menubar\" (keydown)=\"onKeydown($event)\">\n @for (item of items; track item) {\n <li>\n @if (item.href) {\n <a\n #menuItem\n role=\"menuitem\"\n [class]=\"getItemClass(item)\"\n [attr.href]=\"getItemHref(item)\"\n [attr.target]=\"item.target ?? null\"\n [attr.rel]=\"getItemRel(item)\"\n [attr.aria-label]=\"item.ariaLabel ?? null\"\n [attr.aria-current]=\"item.active ? 'page' : null\"\n [attr.aria-disabled]=\"isItemDisabled(item) ? 'true' : null\"\n [attr.tabindex]=\"isItemDisabled(item) ? -1 : null\"\n [attr.data-disabled]=\"isItemDisabled(item) ? '' : null\"\n (click)=\"onItemClick(item, $event)\"\n >\n {{ item.label }}\n </a>\n } @else {\n <button\n #menuItem\n type=\"button\"\n role=\"menuitem\"\n [class]=\"getItemClass(item)\"\n [disabled]=\"isItemDisabled(item)\"\n [attr.aria-label]=\"item.ariaLabel ?? null\"\n [attr.aria-current]=\"item.active ? 'page' : null\"\n [attr.data-disabled]=\"isItemDisabled(item) ? '' : null\"\n (click)=\"onItemClick(item, $event)\"\n >\n {{ item.label }}\n </button>\n }\n </li>\n }\n </ul>\n</nav>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
801
2094
  }
802
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsInputComponent, decorators: [{
2095
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsMenubarComponent, decorators: [{
803
2096
  type: Component,
804
- args: [{ selector: 'cxs-input', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
805
- {
806
- provide: NG_VALUE_ACCESSOR,
807
- useExisting: forwardRef(() => CxsInputComponent),
808
- multi: true
809
- }
810
- ], template: "<input\n [class]=\"inputClass\"\n [attr.id]=\"id\"\n [attr.name]=\"name\"\n [attr.type]=\"type\"\n [attr.placeholder]=\"placeholder\"\n [attr.autocomplete]=\"autocomplete\"\n [attr.inputmode]=\"inputmode\"\n [attr.spellcheck]=\"spellcheck === null ? null : spellcheck\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-describedby]=\"ariaDescribedby\"\n [attr.aria-invalid]=\"invalid ? 'true' : null\"\n [attr.autofocus]=\"autofocus ? true : null\"\n [disabled]=\"isDisabled\"\n [readonly]=\"readonly\"\n [required]=\"required\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n/>\r\n" }]
811
- }], propDecorators: { value: [{
812
- type: Input
813
- }], type: [{
2097
+ args: [{ selector: 'cxs-menubar', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav [attr.aria-label]=\"ariaLabel\">\n <ul [class]=\"menubarClass\" role=\"menubar\" (keydown)=\"onKeydown($event)\">\n @for (item of items; track item) {\n <li>\n @if (item.href) {\n <a\n #menuItem\n role=\"menuitem\"\n [class]=\"getItemClass(item)\"\n [attr.href]=\"getItemHref(item)\"\n [attr.target]=\"item.target ?? null\"\n [attr.rel]=\"getItemRel(item)\"\n [attr.aria-label]=\"item.ariaLabel ?? null\"\n [attr.aria-current]=\"item.active ? 'page' : null\"\n [attr.aria-disabled]=\"isItemDisabled(item) ? 'true' : null\"\n [attr.tabindex]=\"isItemDisabled(item) ? -1 : null\"\n [attr.data-disabled]=\"isItemDisabled(item) ? '' : null\"\n (click)=\"onItemClick(item, $event)\"\n >\n {{ item.label }}\n </a>\n } @else {\n <button\n #menuItem\n type=\"button\"\n role=\"menuitem\"\n [class]=\"getItemClass(item)\"\n [disabled]=\"isItemDisabled(item)\"\n [attr.aria-label]=\"item.ariaLabel ?? null\"\n [attr.aria-current]=\"item.active ? 'page' : null\"\n [attr.data-disabled]=\"isItemDisabled(item) ? '' : null\"\n (click)=\"onItemClick(item, $event)\"\n >\n {{ item.label }}\n </button>\n }\n </li>\n }\n </ul>\n</nav>\n" }]
2098
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
2099
+ type: Attribute,
2100
+ args: ['class']
2101
+ }] }], propDecorators: { items: [{
814
2102
  type: Input
815
- }], variant: [{
2103
+ }], ariaLabel: [{
816
2104
  type: Input
817
2105
  }], size: [{
818
2106
  type: Input
819
- }], disabled: [{
820
- type: Input
821
- }], readonly: [{
822
- type: Input
823
- }], required: [{
824
- type: Input
825
- }], invalid: [{
826
- type: Input
827
- }], autofocus: [{
828
- type: Input
829
- }], id: [{
830
- type: Input
831
- }], name: [{
832
- type: Input
833
- }], placeholder: [{
834
- type: Input
835
- }], autocomplete: [{
836
- type: Input
837
- }], inputmode: [{
838
- type: Input
839
- }], spellcheck: [{
840
- type: Input
841
- }], ariaLabel: [{
2107
+ }], variant: [{
842
2108
  type: Input
843
- }], ariaDescribedby: [{
2109
+ }], disabled: [{
844
2110
  type: Input
845
- }], valueChange: [{
2111
+ }], itemSelected: [{
846
2112
  type: Output
2113
+ }], menuItems: [{
2114
+ type: ViewChildren,
2115
+ args: ['menuItem', { read: ElementRef }]
847
2116
  }] } });
848
2117
 
849
2118
  const WRAPPER_BASE = 'inline-flex items-center gap-2 select-none';
@@ -958,7 +2227,7 @@ class CxsRadioComponent {
958
2227
  useExisting: forwardRef(() => CxsRadioComponent),
959
2228
  multi: true
960
2229
  }
961
- ], ngImport: i0, template: "<label [class]=\"wrapperClass\">\n <input\n class=\"peer sr-only\"\n type=\"radio\"\n [attr.id]=\"id\"\n [attr.name]=\"name\"\n [attr.value]=\"value\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-describedby]=\"ariaDescribedby\"\n [attr.aria-invalid]=\"invalid ? 'true' : null\"\n [attr.autofocus]=\"autofocus ? true : null\"\n [checked]=\"isChecked\"\n [disabled]=\"isDisabled\"\n [required]=\"required\"\n (change)=\"onRadioChange($event)\"\n (blur)=\"onBlur()\"\n />\n <span [class]=\"controlClass\" aria-hidden=\"true\"></span>\n <span [class]=\"labelClass\">\n <ng-content></ng-content>\n </span>\n</label>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
2230
+ ], ngImport: i0, template: "<label [class]=\"wrapperClass\">\r\n <input\r\n class=\"peer sr-only\"\r\n type=\"radio\"\r\n [attr.id]=\"id\"\r\n [attr.name]=\"name\"\r\n [attr.value]=\"value\"\r\n [attr.aria-label]=\"ariaLabel\"\r\n [attr.aria-describedby]=\"ariaDescribedby\"\r\n [attr.aria-invalid]=\"invalid ? 'true' : null\"\r\n [attr.autofocus]=\"autofocus ? true : null\"\r\n [checked]=\"isChecked\"\r\n [disabled]=\"isDisabled\"\r\n [required]=\"required\"\r\n (change)=\"onRadioChange($event)\"\r\n (blur)=\"onBlur()\"\r\n />\r\n <span [class]=\"controlClass\" aria-hidden=\"true\"></span>\r\n <span [class]=\"labelClass\">\r\n <ng-content></ng-content>\r\n </span>\r\n</label>\r\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
962
2231
  }
963
2232
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsRadioComponent, decorators: [{
964
2233
  type: Component,
@@ -968,7 +2237,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
968
2237
  useExisting: forwardRef(() => CxsRadioComponent),
969
2238
  multi: true
970
2239
  }
971
- ], template: "<label [class]=\"wrapperClass\">\n <input\n class=\"peer sr-only\"\n type=\"radio\"\n [attr.id]=\"id\"\n [attr.name]=\"name\"\n [attr.value]=\"value\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-describedby]=\"ariaDescribedby\"\n [attr.aria-invalid]=\"invalid ? 'true' : null\"\n [attr.autofocus]=\"autofocus ? true : null\"\n [checked]=\"isChecked\"\n [disabled]=\"isDisabled\"\n [required]=\"required\"\n (change)=\"onRadioChange($event)\"\n (blur)=\"onBlur()\"\n />\n <span [class]=\"controlClass\" aria-hidden=\"true\"></span>\n <span [class]=\"labelClass\">\n <ng-content></ng-content>\n </span>\n</label>\n" }]
2240
+ ], template: "<label [class]=\"wrapperClass\">\r\n <input\r\n class=\"peer sr-only\"\r\n type=\"radio\"\r\n [attr.id]=\"id\"\r\n [attr.name]=\"name\"\r\n [attr.value]=\"value\"\r\n [attr.aria-label]=\"ariaLabel\"\r\n [attr.aria-describedby]=\"ariaDescribedby\"\r\n [attr.aria-invalid]=\"invalid ? 'true' : null\"\r\n [attr.autofocus]=\"autofocus ? true : null\"\r\n [checked]=\"isChecked\"\r\n [disabled]=\"isDisabled\"\r\n [required]=\"required\"\r\n (change)=\"onRadioChange($event)\"\r\n (blur)=\"onBlur()\"\r\n />\r\n <span [class]=\"controlClass\" aria-hidden=\"true\"></span>\r\n <span [class]=\"labelClass\">\r\n <ng-content></ng-content>\r\n </span>\r\n</label>\r\n" }]
972
2241
  }], propDecorators: { value: [{
973
2242
  type: Input
974
2243
  }], name: [{
@@ -995,13 +2264,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
995
2264
  type: Output
996
2265
  }] } });
997
2266
 
998
- const BASE_CLASSES = 'w-full appearance-none rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
2267
+ const BASE_CLASSES$1 = 'w-full appearance-none rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
999
2268
  'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)] ' +
1000
2269
  'placeholder:text-[var(--cxs-color-on-surface-muted)] transition-colors ' +
1001
2270
  'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
1002
2271
  'focus-visible:outline-[var(--cxs-color-focus)] focus-visible:border-[var(--cxs-color-focus)] ' +
1003
2272
  'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-60';
1004
- const VARIANT_CLASSES = {
2273
+ const VARIANT_CLASSES$1 = {
1005
2274
  outline: 'bg-[var(--cxs-color-surface)]',
1006
2275
  filled: 'bg-[var(--cxs-color-surface-hover)]'
1007
2276
  };
@@ -1034,9 +2303,9 @@ class CxsSelectComponent {
1034
2303
  }
1035
2304
  get selectClass() {
1036
2305
  return [
1037
- BASE_CLASSES,
2306
+ BASE_CLASSES$1,
1038
2307
  SIZE_CLASSES[this.size],
1039
- VARIANT_CLASSES[this.variant],
2308
+ VARIANT_CLASSES$1[this.variant],
1040
2309
  this.invalid ? INVALID_CLASSES : ''
1041
2310
  ]
1042
2311
  .filter(Boolean)
@@ -1077,7 +2346,7 @@ class CxsSelectComponent {
1077
2346
  useExisting: forwardRef(() => CxsSelectComponent),
1078
2347
  multi: true
1079
2348
  }
1080
- ], ngImport: i0, template: "<div class=\"relative\">\n <select\n [class]=\"selectClass\"\n [attr.id]=\"id\"\n [attr.name]=\"name\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-describedby]=\"ariaDescribedby\"\n [attr.aria-invalid]=\"invalid ? 'true' : null\"\n [disabled]=\"isDisabled\"\n [required]=\"required\"\n [value]=\"selectValue\"\n (change)=\"onSelectChange($event)\"\n (blur)=\"onBlur()\"\n >\n <option *ngIf=\"placeholder\" [value]=\"placeholderValue\" disabled>\n {{ placeholder }}\n </option>\n <ng-content></ng-content>\n </select>\n <span\n class=\"pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-[var(--cxs-color-on-surface-muted)]\"\n aria-hidden=\"true\"\n >\n <svg viewBox=\"0 0 20 20\" fill=\"currentColor\" class=\"h-4 w-4\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 10.94l3.71-3.71a.75.75 0 1 1 1.06 1.06l-4.24 4.25a.75.75 0 0 1-1.06 0L5.21 8.29a.75.75 0 0 1 .02-1.08Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </span>\n</div>\r\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2349
+ ], ngImport: i0, template: "<div class=\"relative\">\r\n <select\r\n [class]=\"selectClass\"\r\n [attr.id]=\"id\"\r\n [attr.name]=\"name\"\r\n [attr.aria-label]=\"ariaLabel\"\r\n [attr.aria-describedby]=\"ariaDescribedby\"\r\n [attr.aria-invalid]=\"invalid ? 'true' : null\"\r\n [disabled]=\"isDisabled\"\r\n [required]=\"required\"\r\n [value]=\"selectValue\"\r\n (change)=\"onSelectChange($event)\"\r\n (blur)=\"onBlur()\"\r\n >\r\n <option *ngIf=\"placeholder\" [value]=\"placeholderValue\" disabled>\r\n {{ placeholder }}\r\n </option>\r\n <ng-content></ng-content>\r\n </select>\r\n <span\r\n class=\"pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-[var(--cxs-color-on-surface-muted)]\"\r\n aria-hidden=\"true\"\r\n >\r\n <svg viewBox=\"0 0 20 20\" fill=\"currentColor\" class=\"h-4 w-4\">\r\n <path\r\n fill-rule=\"evenodd\"\r\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 10.94l3.71-3.71a.75.75 0 1 1 1.06 1.06l-4.24 4.25a.75.75 0 0 1-1.06 0L5.21 8.29a.75.75 0 0 1 .02-1.08Z\"\r\n clip-rule=\"evenodd\"\r\n />\r\n </svg>\r\n </span>\r\n</div>\r\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1081
2350
  }
1082
2351
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsSelectComponent, decorators: [{
1083
2352
  type: Component,
@@ -1087,7 +2356,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1087
2356
  useExisting: forwardRef(() => CxsSelectComponent),
1088
2357
  multi: true
1089
2358
  }
1090
- ], template: "<div class=\"relative\">\n <select\n [class]=\"selectClass\"\n [attr.id]=\"id\"\n [attr.name]=\"name\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-describedby]=\"ariaDescribedby\"\n [attr.aria-invalid]=\"invalid ? 'true' : null\"\n [disabled]=\"isDisabled\"\n [required]=\"required\"\n [value]=\"selectValue\"\n (change)=\"onSelectChange($event)\"\n (blur)=\"onBlur()\"\n >\n <option *ngIf=\"placeholder\" [value]=\"placeholderValue\" disabled>\n {{ placeholder }}\n </option>\n <ng-content></ng-content>\n </select>\n <span\n class=\"pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-[var(--cxs-color-on-surface-muted)]\"\n aria-hidden=\"true\"\n >\n <svg viewBox=\"0 0 20 20\" fill=\"currentColor\" class=\"h-4 w-4\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 10.94l3.71-3.71a.75.75 0 1 1 1.06 1.06l-4.24 4.25a.75.75 0 0 1-1.06 0L5.21 8.29a.75.75 0 0 1 .02-1.08Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </span>\n</div>\r\n" }]
2359
+ ], template: "<div class=\"relative\">\r\n <select\r\n [class]=\"selectClass\"\r\n [attr.id]=\"id\"\r\n [attr.name]=\"name\"\r\n [attr.aria-label]=\"ariaLabel\"\r\n [attr.aria-describedby]=\"ariaDescribedby\"\r\n [attr.aria-invalid]=\"invalid ? 'true' : null\"\r\n [disabled]=\"isDisabled\"\r\n [required]=\"required\"\r\n [value]=\"selectValue\"\r\n (change)=\"onSelectChange($event)\"\r\n (blur)=\"onBlur()\"\r\n >\r\n <option *ngIf=\"placeholder\" [value]=\"placeholderValue\" disabled>\r\n {{ placeholder }}\r\n </option>\r\n <ng-content></ng-content>\r\n </select>\r\n <span\r\n class=\"pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-[var(--cxs-color-on-surface-muted)]\"\r\n aria-hidden=\"true\"\r\n >\r\n <svg viewBox=\"0 0 20 20\" fill=\"currentColor\" class=\"h-4 w-4\">\r\n <path\r\n fill-rule=\"evenodd\"\r\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 10.94l3.71-3.71a.75.75 0 1 1 1.06 1.06l-4.24 4.25a.75.75 0 0 1-1.06 0L5.21 8.29a.75.75 0 0 1 .02-1.08Z\"\r\n clip-rule=\"evenodd\"\r\n />\r\n </svg>\r\n </span>\r\n</div>\r\n" }]
1091
2360
  }], propDecorators: { value: [{
1092
2361
  type: Input
1093
2362
  }], variant: [{
@@ -1116,6 +2385,446 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1116
2385
  type: Output
1117
2386
  }] } });
1118
2387
 
2388
+ const WRAPPER_BASE_CLASSES = 'w-full overflow-hidden';
2389
+ const WRAPPER_BORDERED_CLASSES = 'rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)]';
2390
+ const TABLE_BASE_CLASSES = 'w-full border-collapse text-sm text-[var(--cxs-color-on-surface)]';
2391
+ const TABLE_COMPACT_CLASSES = 'text-xs';
2392
+ const HEADER_ROW_CLASSES = 'border-b border-[var(--cxs-color-border)] text-xs uppercase tracking-[0.2em] ' +
2393
+ 'text-[var(--cxs-color-on-surface-muted)]';
2394
+ const HEADER_CELL_BASE_CLASSES = 'px-3 py-2 text-left font-semibold';
2395
+ const BODY_ROW_BASE_CLASSES = 'border-b border-[var(--cxs-color-border)] last:border-b-0';
2396
+ const BODY_ROW_STRIPED_CLASSES = 'odd:bg-[var(--cxs-color-surface)] even:bg-[var(--cxs-color-surface-hover)]';
2397
+ const BODY_CELL_BASE_CLASSES = 'px-3 py-2 align-top';
2398
+ const BODY_CELL_COMPACT_CLASSES = 'px-2 py-1.5';
2399
+ const ALIGN_CLASSES = {
2400
+ left: 'text-left',
2401
+ center: 'text-center',
2402
+ right: 'text-right'
2403
+ };
2404
+ const EMPTY_ROW_CLASSES = 'text-sm text-[var(--cxs-color-on-surface-muted)]';
2405
+ class CxsTableComponent {
2406
+ hostClass;
2407
+ columns = [];
2408
+ data = [];
2409
+ caption;
2410
+ ariaLabel = 'Table';
2411
+ striped = false;
2412
+ bordered = false;
2413
+ compact = false;
2414
+ emptyMessage = 'No data available';
2415
+ emptyCell = '—';
2416
+ constructor(hostClass) {
2417
+ this.hostClass = hostClass;
2418
+ }
2419
+ get wrapperClass() {
2420
+ return [
2421
+ WRAPPER_BASE_CLASSES,
2422
+ this.bordered ? WRAPPER_BORDERED_CLASSES : '',
2423
+ this.hostClass
2424
+ ]
2425
+ .filter(Boolean)
2426
+ .join(' ');
2427
+ }
2428
+ get tableClass() {
2429
+ return [TABLE_BASE_CLASSES, this.compact ? TABLE_COMPACT_CLASSES : '']
2430
+ .filter(Boolean)
2431
+ .join(' ');
2432
+ }
2433
+ get headerRowClass() {
2434
+ return HEADER_ROW_CLASSES;
2435
+ }
2436
+ get bodyRowClass() {
2437
+ return [
2438
+ BODY_ROW_BASE_CLASSES,
2439
+ this.striped ? BODY_ROW_STRIPED_CLASSES : ''
2440
+ ]
2441
+ .filter(Boolean)
2442
+ .join(' ');
2443
+ }
2444
+ get emptyRowClass() {
2445
+ return EMPTY_ROW_CLASSES;
2446
+ }
2447
+ get colSpan() {
2448
+ return Math.max(this.columns.length, 1);
2449
+ }
2450
+ headerCellClass(column) {
2451
+ return [
2452
+ HEADER_CELL_BASE_CLASSES,
2453
+ ALIGN_CLASSES[column.align ?? 'left'],
2454
+ column.headerClass ?? ''
2455
+ ]
2456
+ .filter(Boolean)
2457
+ .join(' ');
2458
+ }
2459
+ bodyCellClass(column) {
2460
+ return [
2461
+ BODY_CELL_BASE_CLASSES,
2462
+ this.compact ? BODY_CELL_COMPACT_CLASSES : '',
2463
+ ALIGN_CLASSES[column.align ?? 'left'],
2464
+ column.cellClass ?? ''
2465
+ ]
2466
+ .filter(Boolean)
2467
+ .join(' ');
2468
+ }
2469
+ cellValue(row, column) {
2470
+ const value = row[column.key];
2471
+ if (value === null || value === undefined || value === '') {
2472
+ return this.emptyCell;
2473
+ }
2474
+ return String(value);
2475
+ }
2476
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsTableComponent, deps: [{ token: 'class', attribute: true }], target: i0.ɵɵFactoryTarget.Component });
2477
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CxsTableComponent, isStandalone: true, selector: "cxs-table", inputs: { columns: "columns", data: "data", caption: "caption", ariaLabel: "ariaLabel", striped: "striped", bordered: "bordered", compact: "compact", emptyMessage: "emptyMessage", emptyCell: "emptyCell" }, ngImport: i0, template: "<div [class]=\"wrapperClass\">\n <table [class]=\"tableClass\" [attr.aria-label]=\"ariaLabel\">\n @if (caption) {\n <caption class=\"sr-only\">{{ caption }}</caption>\n }\n <thead>\n <tr [class]=\"headerRowClass\">\n @for (column of columns; track column) {\n <th\n [class]=\"headerCellClass(column)\"\n [style.width]=\"column.width ?? null\"\n scope=\"col\"\n >\n {{ column.label }}\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @if (data.length === 0) {\n <tr [class]=\"bodyRowClass\">\n <td [class]=\"bodyCellClass({ key: '', label: '' })\" [attr.colspan]=\"colSpan\">\n <span [class]=\"emptyRowClass\">{{ emptyMessage }}</span>\n </td>\n </tr>\n } @else {\n @for (row of data; track row; let rowIndex = $index) {\n <tr [class]=\"bodyRowClass\">\n @for (column of columns; track column) {\n <td [class]=\"bodyCellClass(column)\">\n {{ cellValue(row, column) }}\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
2478
+ }
2479
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsTableComponent, decorators: [{
2480
+ type: Component,
2481
+ args: [{ selector: 'cxs-table', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class]=\"wrapperClass\">\n <table [class]=\"tableClass\" [attr.aria-label]=\"ariaLabel\">\n @if (caption) {\n <caption class=\"sr-only\">{{ caption }}</caption>\n }\n <thead>\n <tr [class]=\"headerRowClass\">\n @for (column of columns; track column) {\n <th\n [class]=\"headerCellClass(column)\"\n [style.width]=\"column.width ?? null\"\n scope=\"col\"\n >\n {{ column.label }}\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @if (data.length === 0) {\n <tr [class]=\"bodyRowClass\">\n <td [class]=\"bodyCellClass({ key: '', label: '' })\" [attr.colspan]=\"colSpan\">\n <span [class]=\"emptyRowClass\">{{ emptyMessage }}</span>\n </td>\n </tr>\n } @else {\n @for (row of data; track row; let rowIndex = $index) {\n <tr [class]=\"bodyRowClass\">\n @for (column of columns; track column) {\n <td [class]=\"bodyCellClass(column)\">\n {{ cellValue(row, column) }}\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n" }]
2482
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
2483
+ type: Attribute,
2484
+ args: ['class']
2485
+ }] }], propDecorators: { columns: [{
2486
+ type: Input
2487
+ }], data: [{
2488
+ type: Input
2489
+ }], caption: [{
2490
+ type: Input
2491
+ }], ariaLabel: [{
2492
+ type: Input
2493
+ }], striped: [{
2494
+ type: Input
2495
+ }], bordered: [{
2496
+ type: Input
2497
+ }], compact: [{
2498
+ type: Input
2499
+ }], emptyMessage: [{
2500
+ type: Input
2501
+ }], emptyCell: [{
2502
+ type: Input
2503
+ }] } });
2504
+
2505
+ const WRAPPER_CLASSES$1 = 'inline-flex items-center gap-2';
2506
+ const LABEL_BASE_CLASSES = 'text-sm text-[var(--cxs-color-on-surface)] select-none';
2507
+ const LABEL_DISABLED_CLASSES = 'text-[var(--cxs-color-on-surface-muted)]';
2508
+ const TRACK_BASE_CLASSES = 'relative inline-flex items-center rounded-full border transition-colors ' +
2509
+ 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
2510
+ 'focus-visible:outline-[var(--cxs-color-focus)] disabled:pointer-events-none disabled:opacity-60';
2511
+ const TRACK_SIZE_CLASSES = {
2512
+ sm: 'h-5 w-9 p-0.5',
2513
+ md: 'h-6 w-11 p-0.5',
2514
+ lg: 'h-8 w-14 p-0.5'
2515
+ };
2516
+ const TRACK_STATE_CLASSES = {
2517
+ on: 'bg-[var(--cxs-color-primary)] border-[var(--cxs-color-primary)]',
2518
+ off: 'bg-[var(--cxs-color-surface)] border-[var(--cxs-color-border)]'
2519
+ };
2520
+ const THUMB_BASE_CLASSES = 'absolute left-0.5 top-0.5 rounded-full transition-transform duration-200 ease-out';
2521
+ const THUMB_SIZE_CLASSES = {
2522
+ sm: 'h-4 w-4',
2523
+ md: 'h-5 w-5',
2524
+ lg: 'h-7 w-7'
2525
+ };
2526
+ const THUMB_STATE_CLASSES = {
2527
+ on: {
2528
+ sm: 'translate-x-[18px] bg-[var(--cxs-color-on-primary)]',
2529
+ md: 'translate-x-[22px] bg-[var(--cxs-color-on-primary)]',
2530
+ lg: 'translate-x-[26px] bg-[var(--cxs-color-on-primary)]'
2531
+ },
2532
+ off: 'translate-x-0 bg-[var(--cxs-color-surface)]'
2533
+ };
2534
+ class CxsToggleComponent {
2535
+ static nextId = 0;
2536
+ checked = false;
2537
+ disabled = false;
2538
+ size = 'md';
2539
+ label;
2540
+ ariaLabel = 'Toggle';
2541
+ checkedChange = new EventEmitter();
2542
+ labelId = `cxs-toggle-label-${CxsToggleComponent.nextId++}`;
2543
+ get wrapperClass() {
2544
+ return WRAPPER_CLASSES$1;
2545
+ }
2546
+ get labelClass() {
2547
+ return [LABEL_BASE_CLASSES, this.disabled ? LABEL_DISABLED_CLASSES : '']
2548
+ .filter(Boolean)
2549
+ .join(' ');
2550
+ }
2551
+ get trackClass() {
2552
+ return [
2553
+ TRACK_BASE_CLASSES,
2554
+ TRACK_SIZE_CLASSES[this.size],
2555
+ this.checked ? TRACK_STATE_CLASSES.on : TRACK_STATE_CLASSES.off
2556
+ ].join(' ');
2557
+ }
2558
+ get thumbClass() {
2559
+ const thumbState = this.checked
2560
+ ? THUMB_STATE_CLASSES.on[this.size]
2561
+ : THUMB_STATE_CLASSES.off;
2562
+ return [THUMB_BASE_CLASSES, THUMB_SIZE_CLASSES[this.size], thumbState].join(' ');
2563
+ }
2564
+ get ariaLabelValue() {
2565
+ if (this.label) {
2566
+ return null;
2567
+ }
2568
+ return this.ariaLabel;
2569
+ }
2570
+ toggle() {
2571
+ if (this.disabled) {
2572
+ return;
2573
+ }
2574
+ this.checkedChange.emit(!this.checked);
2575
+ }
2576
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsToggleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2577
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CxsToggleComponent, isStandalone: true, selector: "cxs-toggle", inputs: { checked: "checked", disabled: "disabled", size: "size", label: "label", ariaLabel: "ariaLabel" }, outputs: { checkedChange: "checkedChange" }, ngImport: i0, template: "<div [class]=\"wrapperClass\">\n <button\n type=\"button\"\n [class]=\"trackClass\"\n role=\"switch\"\n [disabled]=\"disabled\"\n [attr.aria-checked]=\"checked ? 'true' : 'false'\"\n [attr.aria-labelledby]=\"label ? labelId : null\"\n [attr.aria-label]=\"ariaLabelValue\"\n (click)=\"toggle()\"\n >\n <span [class]=\"thumbClass\" aria-hidden=\"true\"></span>\n </button>\n @if (label) {\n <span [id]=\"labelId\" [class]=\"labelClass\">{{ label }}</span>\n }\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
2578
+ }
2579
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsToggleComponent, decorators: [{
2580
+ type: Component,
2581
+ args: [{ selector: 'cxs-toggle', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class]=\"wrapperClass\">\n <button\n type=\"button\"\n [class]=\"trackClass\"\n role=\"switch\"\n [disabled]=\"disabled\"\n [attr.aria-checked]=\"checked ? 'true' : 'false'\"\n [attr.aria-labelledby]=\"label ? labelId : null\"\n [attr.aria-label]=\"ariaLabelValue\"\n (click)=\"toggle()\"\n >\n <span [class]=\"thumbClass\" aria-hidden=\"true\"></span>\n </button>\n @if (label) {\n <span [id]=\"labelId\" [class]=\"labelClass\">{{ label }}</span>\n }\n</div>\n" }]
2582
+ }], propDecorators: { checked: [{
2583
+ type: Input
2584
+ }], disabled: [{
2585
+ type: Input
2586
+ }], size: [{
2587
+ type: Input
2588
+ }], label: [{
2589
+ type: Input
2590
+ }], ariaLabel: [{
2591
+ type: Input
2592
+ }], checkedChange: [{
2593
+ type: Output
2594
+ }] } });
2595
+
2596
+ const HOST_BASE_CLASSES = 'pointer-events-none';
2597
+ const POSITION_CLASSES = {
2598
+ 'bottom-right': 'fixed bottom-6 right-6 items-end',
2599
+ bottom: 'fixed bottom-6 left-1/2 -translate-x-1/2 items-end',
2600
+ 'bottom-left': 'fixed bottom-6 left-6 items-start',
2601
+ 'top-right': 'fixed top-6 right-6 items-end',
2602
+ top: 'fixed top-6 left-1/2 -translate-x-1/2 items-start',
2603
+ 'top-left': 'fixed top-6 left-6 items-start',
2604
+ right: 'fixed right-6 top-1/2 -translate-y-1/2 items-end',
2605
+ left: 'fixed left-6 top-1/2 -translate-y-1/2 items-start',
2606
+ center: 'fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 items-center'
2607
+ };
2608
+ const BASE_CLASSES = 'pointer-events-auto flex w-full items-start gap-3 rounded-[var(--cxs-radius-md)] ' +
2609
+ 'border px-4 py-3 shadow-[var(--cxs-shadow-sm)]';
2610
+ const WIDTH_CLASSES = {
2611
+ sm: 'max-w-sm',
2612
+ md: 'max-w-md',
2613
+ lg: 'max-w-lg',
2614
+ xl: 'max-w-xl',
2615
+ none: 'max-w-none'
2616
+ };
2617
+ const VARIANT_CLASSES = {
2618
+ info: 'border-[var(--cxs-color-primary)] bg-[var(--cxs-color-primary-ghost)] ' +
2619
+ 'text-[var(--cxs-color-on-surface)]',
2620
+ neutral: 'border-[var(--cxs-color-border)] bg-[var(--cxs-color-surface)] ' +
2621
+ 'text-[var(--cxs-color-on-surface)]',
2622
+ danger: 'border-[var(--cxs-color-danger)] bg-[var(--cxs-color-surface)] ' +
2623
+ 'text-[var(--cxs-color-on-surface)]'
2624
+ };
2625
+ const TITLE_CLASSES = 'text-sm font-semibold';
2626
+ const MESSAGE_CLASSES = 'mt-1 text-sm text-[var(--cxs-color-on-surface-muted)]';
2627
+ const ACTIONS_CLASSES = 'mt-3 flex flex-wrap items-center gap-2 empty:hidden';
2628
+ const CLOSE_BUTTON_CLASSES = 'inline-flex h-8 w-8 items-center justify-center rounded-[var(--cxs-radius-md)] ' +
2629
+ 'text-[var(--cxs-color-on-surface)] transition-colors hover:bg-[var(--cxs-color-surface-hover)] ' +
2630
+ 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
2631
+ 'focus-visible:outline-[var(--cxs-color-focus)]';
2632
+ class CxsToastComponent {
2633
+ hostClass;
2634
+ open = false;
2635
+ title;
2636
+ message;
2637
+ ariaLabel = 'Notification';
2638
+ variant = 'info';
2639
+ dismissible = true;
2640
+ duration = 0;
2641
+ position = 'bottom-right';
2642
+ maxWidth = 'sm';
2643
+ openChange = new EventEmitter();
2644
+ dismissed = new EventEmitter();
2645
+ timeoutId = null;
2646
+ constructor(hostClass) {
2647
+ this.hostClass = hostClass;
2648
+ }
2649
+ ngOnChanges(changes) {
2650
+ if (changes['open'] || changes['duration']) {
2651
+ this.resetTimer();
2652
+ }
2653
+ }
2654
+ ngOnDestroy() {
2655
+ this.clearTimer();
2656
+ }
2657
+ get hostClassName() {
2658
+ return [HOST_BASE_CLASSES, POSITION_CLASSES[this.position]].join(' ');
2659
+ }
2660
+ get toastClass() {
2661
+ return [
2662
+ BASE_CLASSES,
2663
+ WIDTH_CLASSES[this.maxWidth],
2664
+ VARIANT_CLASSES[this.variant],
2665
+ this.hostClass
2666
+ ]
2667
+ .filter(Boolean)
2668
+ .join(' ');
2669
+ }
2670
+ get titleClass() {
2671
+ return TITLE_CLASSES;
2672
+ }
2673
+ get messageClass() {
2674
+ return MESSAGE_CLASSES;
2675
+ }
2676
+ get actionsClass() {
2677
+ return ACTIONS_CLASSES;
2678
+ }
2679
+ get closeButtonClass() {
2680
+ return CLOSE_BUTTON_CLASSES;
2681
+ }
2682
+ get role() {
2683
+ return this.variant === 'danger' ? 'alert' : 'status';
2684
+ }
2685
+ get ariaLive() {
2686
+ return this.variant === 'danger' ? 'assertive' : 'polite';
2687
+ }
2688
+ get ariaLabelValue() {
2689
+ if (this.title) {
2690
+ return null;
2691
+ }
2692
+ return this.ariaLabel;
2693
+ }
2694
+ requestClose(reason) {
2695
+ this.openChange.emit(false);
2696
+ this.dismissed.emit(reason);
2697
+ }
2698
+ resetTimer() {
2699
+ this.clearTimer();
2700
+ if (this.open && this.duration > 0) {
2701
+ this.timeoutId = setTimeout(() => {
2702
+ this.requestClose('timeout');
2703
+ }, this.duration);
2704
+ }
2705
+ }
2706
+ clearTimer() {
2707
+ if (this.timeoutId) {
2708
+ clearTimeout(this.timeoutId);
2709
+ this.timeoutId = null;
2710
+ }
2711
+ }
2712
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsToastComponent, deps: [{ token: 'class', attribute: true }], target: i0.ɵɵFactoryTarget.Component });
2713
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CxsToastComponent, isStandalone: true, selector: "cxs-toast", inputs: { open: "open", title: "title", message: "message", ariaLabel: "ariaLabel", variant: "variant", dismissible: "dismissible", duration: "duration", position: "position", maxWidth: "maxWidth" }, outputs: { openChange: "openChange", dismissed: "dismissed" }, usesOnChanges: true, ngImport: i0, template: "@if (open) {\n <div [class]=\"hostClassName\">\n <div\n [class]=\"toastClass\"\n [attr.role]=\"role\"\n [attr.aria-live]=\"ariaLive\"\n [attr.aria-label]=\"ariaLabelValue\"\n >\n <div class=\"flex-1\">\n @if (title) {\n <p [class]=\"titleClass\">{{ title }}</p>\n }\n @if (message) {\n <p [class]=\"messageClass\">{{ message }}</p>\n }\n <ng-content></ng-content>\n <div [class]=\"actionsClass\">\n <ng-content select=\"[cxsToastAction]\"></ng-content>\n </div>\n </div>\n @if (dismissible) {\n <button\n type=\"button\"\n [class]=\"closeButtonClass\"\n aria-label=\"Dismiss notification\"\n (click)=\"requestClose('dismiss')\"\n >\n X\n </button>\n }\n </div>\n </div>\n}\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
2714
+ }
2715
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsToastComponent, decorators: [{
2716
+ type: Component,
2717
+ args: [{ selector: 'cxs-toast', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (open) {\n <div [class]=\"hostClassName\">\n <div\n [class]=\"toastClass\"\n [attr.role]=\"role\"\n [attr.aria-live]=\"ariaLive\"\n [attr.aria-label]=\"ariaLabelValue\"\n >\n <div class=\"flex-1\">\n @if (title) {\n <p [class]=\"titleClass\">{{ title }}</p>\n }\n @if (message) {\n <p [class]=\"messageClass\">{{ message }}</p>\n }\n <ng-content></ng-content>\n <div [class]=\"actionsClass\">\n <ng-content select=\"[cxsToastAction]\"></ng-content>\n </div>\n </div>\n @if (dismissible) {\n <button\n type=\"button\"\n [class]=\"closeButtonClass\"\n aria-label=\"Dismiss notification\"\n (click)=\"requestClose('dismiss')\"\n >\n X\n </button>\n }\n </div>\n </div>\n}\n" }]
2718
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
2719
+ type: Attribute,
2720
+ args: ['class']
2721
+ }] }], propDecorators: { open: [{
2722
+ type: Input
2723
+ }], title: [{
2724
+ type: Input
2725
+ }], message: [{
2726
+ type: Input
2727
+ }], ariaLabel: [{
2728
+ type: Input
2729
+ }], variant: [{
2730
+ type: Input
2731
+ }], dismissible: [{
2732
+ type: Input
2733
+ }], duration: [{
2734
+ type: Input
2735
+ }], position: [{
2736
+ type: Input
2737
+ }], maxWidth: [{
2738
+ type: Input
2739
+ }], openChange: [{
2740
+ type: Output
2741
+ }], dismissed: [{
2742
+ type: Output
2743
+ }] } });
2744
+
2745
+ const WRAPPER_CLASSES = 'relative inline-flex';
2746
+ const TOOLTIP_BASE_CLASSES = 'pointer-events-none absolute z-50 max-w-xs rounded-[var(--cxs-radius-md)] px-2 py-1 text-xs ' +
2747
+ 'text-[var(--cxs-color-on-primary)] bg-[var(--cxs-color-on-surface)] shadow-[var(--cxs-shadow-sm)]';
2748
+ const TOOLTIP_POSITION_CLASSES = {
2749
+ top: 'bottom-full left-1/2 mb-2 -translate-x-1/2',
2750
+ right: 'left-full top-1/2 ml-2 -translate-y-1/2',
2751
+ bottom: 'top-full left-1/2 mt-2 -translate-x-1/2',
2752
+ left: 'right-full top-1/2 mr-2 -translate-y-1/2'
2753
+ };
2754
+ class CxsTooltipComponent {
2755
+ static nextId = 0;
2756
+ text = '';
2757
+ position = 'top';
2758
+ disabled = false;
2759
+ open = false;
2760
+ tooltipId = `cxs-tooltip-${CxsTooltipComponent.nextId++}`;
2761
+ get wrapperClass() {
2762
+ return WRAPPER_CLASSES;
2763
+ }
2764
+ get tooltipClass() {
2765
+ return [TOOLTIP_BASE_CLASSES, TOOLTIP_POSITION_CLASSES[this.position]].join(' ');
2766
+ }
2767
+ get describedBy() {
2768
+ if (!this.text || this.disabled) {
2769
+ return null;
2770
+ }
2771
+ return this.tooltipId;
2772
+ }
2773
+ show() {
2774
+ if (this.disabled || !this.text) {
2775
+ return;
2776
+ }
2777
+ this.open = true;
2778
+ }
2779
+ hide() {
2780
+ this.open = false;
2781
+ }
2782
+ onMouseEnter() {
2783
+ this.show();
2784
+ }
2785
+ onMouseLeave() {
2786
+ this.hide();
2787
+ }
2788
+ onFocusIn() {
2789
+ this.show();
2790
+ }
2791
+ onFocusOut() {
2792
+ this.hide();
2793
+ }
2794
+ onKeydown(event) {
2795
+ if (event.key === 'Escape') {
2796
+ this.hide();
2797
+ }
2798
+ }
2799
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsTooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2800
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CxsTooltipComponent, isStandalone: true, selector: "cxs-tooltip", inputs: { text: "text", position: "position", disabled: "disabled" }, host: { listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()", "focusin": "onFocusIn()", "focusout": "onFocusOut()", "keydown": "onKeydown($event)" } }, ngImport: i0, template: "<span [class]=\"wrapperClass\">\n <span [attr.aria-describedby]=\"describedBy\">\n <ng-content></ng-content>\n </span>\n @if (open) {\n <span [id]=\"tooltipId\" role=\"tooltip\" [class]=\"tooltipClass\">\n {{ text }}\n </span>\n }\n</span>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
2801
+ }
2802
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsTooltipComponent, decorators: [{
2803
+ type: Component,
2804
+ args: [{ selector: 'cxs-tooltip', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<span [class]=\"wrapperClass\">\n <span [attr.aria-describedby]=\"describedBy\">\n <ng-content></ng-content>\n </span>\n @if (open) {\n <span [id]=\"tooltipId\" role=\"tooltip\" [class]=\"tooltipClass\">\n {{ text }}\n </span>\n }\n</span>\n" }]
2805
+ }], propDecorators: { text: [{
2806
+ type: Input
2807
+ }], position: [{
2808
+ type: Input
2809
+ }], disabled: [{
2810
+ type: Input
2811
+ }], onMouseEnter: [{
2812
+ type: HostListener,
2813
+ args: ['mouseenter']
2814
+ }], onMouseLeave: [{
2815
+ type: HostListener,
2816
+ args: ['mouseleave']
2817
+ }], onFocusIn: [{
2818
+ type: HostListener,
2819
+ args: ['focusin']
2820
+ }], onFocusOut: [{
2821
+ type: HostListener,
2822
+ args: ['focusout']
2823
+ }], onKeydown: [{
2824
+ type: HostListener,
2825
+ args: ['keydown', ['$event']]
2826
+ }] } });
2827
+
1119
2828
  const DEFAULT_DARK_CLASS = 'cxs-theme-dark';
1120
2829
  class CxsThemeDirective {
1121
2830
  host;
@@ -1223,5 +2932,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1223
2932
  * Generated bundle index. Do not edit.
1224
2933
  */
1225
2934
 
1226
- export { CerxosUi, CxsAlertComponent, CxsBadgeComponent, CxsBreadcrumbComponent, CxsButtonComponent, CxsCardComponent, CxsCheckboxComponent, CxsDatepickerComponent, CxsInputComponent, CxsRadioComponent, CxsSelectComponent, CxsThemeDirective };
2935
+ export { CerxosUi, CxsAccordionComponent, CxsAccordionItemComponent, CxsAlertComponent, CxsAvatarComponent, CxsBadgeComponent, CxsBreadcrumbComponent, CxsButtonComponent, CxsCardComponent, CxsCarouselComponent, CxsCheckboxComponent, CxsDataTableCellDirective, CxsDataTableComponent, CxsDatepickerComponent, CxsDialogComponent, CxsInputComponent, CxsMenubarComponent, CxsRadioComponent, CxsSelectComponent, CxsTableComponent, CxsThemeDirective, CxsToastComponent, CxsToggleComponent, CxsTooltipComponent };
1227
2936
  //# sourceMappingURL=cerxos-ui.mjs.map