cerxos-ui 0.0.3 → 0.0.4

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,5 +1,5 @@
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, ViewChild, HostListener, ElementRef, ViewChildren, Directive } from '@angular/core';
3
3
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
4
4
  import { NgIf } from '@angular/common';
5
5
 
@@ -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,1073 @@ 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
+ const WRAPPER_BASE_CLASSES$1 = 'w-full overflow-hidden';
784
+ const WRAPPER_BORDERED_CLASSES$1 = 'rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)]';
785
+ const TABLE_BASE_CLASSES$1 = 'w-full border-collapse text-sm text-[var(--cxs-color-on-surface)]';
786
+ const TABLE_COMPACT_CLASSES$1 = 'text-xs';
787
+ const HEADER_ROW_CLASSES$1 = 'border-b border-[var(--cxs-color-border)] text-xs font-semibold ' +
788
+ 'text-[var(--cxs-color-on-surface-muted)]';
789
+ const HEADER_CELL_BASE_CLASSES$1 = 'px-3 py-2 text-left';
790
+ const HEADER_BUTTON_CLASSES = 'inline-flex items-center gap-2 rounded-[var(--cxs-radius-md)] px-2 py-1 ' +
791
+ 'transition-colors hover:bg-[var(--cxs-color-surface-hover)] ' +
792
+ 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
793
+ 'focus-visible:outline-[var(--cxs-color-focus)]';
794
+ const HEADER_FILTER_INPUT_CLASSES = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
795
+ 'bg-[var(--cxs-color-surface)] px-2 py-1 text-xs text-[var(--cxs-color-on-surface)] ' +
796
+ 'placeholder:text-[var(--cxs-color-on-surface-muted)] focus-visible:outline focus-visible:outline-2 ' +
797
+ 'focus-visible:outline-offset-2 focus-visible:outline-[var(--cxs-color-focus)]';
798
+ const BODY_ROW_BASE_CLASSES$1 = 'border-b border-[var(--cxs-color-border)] last:border-b-0';
799
+ const BODY_ROW_STRIPED_CLASSES$1 = 'odd:bg-[var(--cxs-color-surface)] even:bg-[var(--cxs-color-surface-hover)]';
800
+ const BODY_CELL_BASE_CLASSES$1 = 'px-3 py-2 align-top';
801
+ const BODY_CELL_COMPACT_CLASSES$1 = 'px-2 py-1.5';
802
+ const ALIGN_CLASSES$1 = {
803
+ left: 'text-left',
804
+ center: 'text-center',
805
+ right: 'text-right'
806
+ };
807
+ const EMPTY_ROW_CLASSES$1 = 'text-sm text-[var(--cxs-color-on-surface-muted)]';
808
+ const PAGINATION_CLASSES = 'mt-4 flex flex-wrap items-center justify-between gap-3 text-sm text-[var(--cxs-color-on-surface)]';
809
+ const PAGE_BUTTON_CLASSES = 'inline-flex items-center justify-center rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
810
+ 'px-3 py-1.5 text-sm transition-colors hover:bg-[var(--cxs-color-surface-hover)] ' +
811
+ 'disabled:cursor-not-allowed disabled:opacity-60';
812
+ const PAGE_SELECT_CLASSES = 'rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] bg-[var(--cxs-color-surface)] ' +
813
+ 'px-2 py-1 text-sm';
814
+ const TOOLBAR_CLASSES = 'mb-3 flex flex-wrap items-center justify-between gap-3 text-sm text-[var(--cxs-color-on-surface)]';
815
+ const TOOLBAR_GROUP_CLASSES = 'flex flex-wrap items-center gap-2';
816
+ const TOOLBAR_INPUT_CLASSES = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
817
+ 'bg-[var(--cxs-color-surface)] px-3 py-2 text-sm text-[var(--cxs-color-on-surface)] ' +
818
+ 'placeholder:text-[var(--cxs-color-on-surface-muted)] focus-visible:outline focus-visible:outline-2 ' +
819
+ 'focus-visible:outline-offset-2 focus-visible:outline-[var(--cxs-color-focus)]';
820
+ const TOOLBAR_BUTTON_CLASSES = 'inline-flex items-center justify-center rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
821
+ 'px-3 py-2 text-sm transition-colors hover:bg-[var(--cxs-color-surface-hover)]';
822
+ const BULK_BAR_CLASSES = 'mb-3 flex flex-wrap items-center justify-between gap-3 rounded-[var(--cxs-radius-md)] ' +
823
+ 'border border-[var(--cxs-color-border)] bg-[var(--cxs-color-surface)] px-3 py-2 text-sm';
824
+ const RESIZE_HANDLE_CLASSES = 'absolute right-0 top-0 h-full w-2 cursor-col-resize touch-none';
825
+ class CxsDataTableComponent {
826
+ hostClass;
827
+ columns = [];
828
+ data = [];
829
+ caption;
830
+ ariaLabel = 'Data table';
831
+ striped = false;
832
+ bordered = false;
833
+ compact = false;
834
+ loading = false;
835
+ emptyMessage = 'No data available';
836
+ emptyCell = '—';
837
+ pageSize = 10;
838
+ pageIndex = 1;
839
+ pageSizeOptions = [10, 20, 50];
840
+ showPagination = true;
841
+ showPageSize = true;
842
+ manualPagination = false;
843
+ total;
844
+ sortKey;
845
+ sortDirection = 'asc';
846
+ manualSort = false;
847
+ showToolbar = true;
848
+ showFilters = true;
849
+ showColumnVisibility = true;
850
+ showGlobalSearch = true;
851
+ manualFilter = false;
852
+ selectable = false;
853
+ rowKey = 'id';
854
+ selectedKeys = [];
855
+ bulkActions = [];
856
+ pageChange = new EventEmitter();
857
+ pageSizeChange = new EventEmitter();
858
+ sortChange = new EventEmitter();
859
+ filterChange = new EventEmitter();
860
+ selectionChange = new EventEmitter();
861
+ bulkAction = new EventEmitter();
862
+ currentPage = 1;
863
+ currentPageSize = 10;
864
+ globalFilter = '';
865
+ columnFilters = {};
866
+ columnVisibility = {};
867
+ columnWidths = {};
868
+ isResizing = false;
869
+ resizingColumn;
870
+ pinnedOffsetsLeft = {};
871
+ pinnedOffsetsRight = {};
872
+ filtersOpen = true;
873
+ resizeStartX = 0;
874
+ resizeStartWidth = 0;
875
+ constructor(hostClass) {
876
+ this.hostClass = hostClass;
877
+ }
878
+ ngOnChanges(changes) {
879
+ if (changes['pageIndex']) {
880
+ this.currentPage = Math.max(1, this.pageIndex);
881
+ }
882
+ if (changes['pageSize']) {
883
+ this.currentPageSize = Math.max(1, this.pageSize);
884
+ }
885
+ if (changes['columns']) {
886
+ this.initializeVisibility();
887
+ this.initializeWidths();
888
+ this.updatePinnedOffsets();
889
+ }
890
+ if (changes['showFilters']) {
891
+ this.filtersOpen = this.showFilters;
892
+ }
893
+ if (changes['selectedKeys']) {
894
+ this.selectedKeys = [...this.selectedKeys];
895
+ }
896
+ }
897
+ get wrapperClass() {
898
+ return [
899
+ WRAPPER_BASE_CLASSES$1,
900
+ this.bordered ? WRAPPER_BORDERED_CLASSES$1 : '',
901
+ this.hostClass
902
+ ]
903
+ .filter(Boolean)
904
+ .join(' ');
905
+ }
906
+ get tableClass() {
907
+ return [TABLE_BASE_CLASSES$1, this.compact ? TABLE_COMPACT_CLASSES$1 : '']
908
+ .filter(Boolean)
909
+ .join(' ');
910
+ }
911
+ get headerRowClass() {
912
+ return HEADER_ROW_CLASSES$1;
913
+ }
914
+ get bodyRowClass() {
915
+ return [
916
+ BODY_ROW_BASE_CLASSES$1,
917
+ this.striped ? BODY_ROW_STRIPED_CLASSES$1 : ''
918
+ ]
919
+ .filter(Boolean)
920
+ .join(' ');
921
+ }
922
+ get emptyRowClass() {
923
+ return EMPTY_ROW_CLASSES$1;
924
+ }
925
+ get paginationClass() {
926
+ return PAGINATION_CLASSES;
927
+ }
928
+ get pageButtonClass() {
929
+ return PAGE_BUTTON_CLASSES;
930
+ }
931
+ get pageSelectClass() {
932
+ return PAGE_SELECT_CLASSES;
933
+ }
934
+ get toolbarClass() {
935
+ return TOOLBAR_CLASSES;
936
+ }
937
+ get toolbarGroupClass() {
938
+ return TOOLBAR_GROUP_CLASSES;
939
+ }
940
+ get toolbarInputClass() {
941
+ return TOOLBAR_INPUT_CLASSES;
942
+ }
943
+ get toolbarButtonClass() {
944
+ return TOOLBAR_BUTTON_CLASSES;
945
+ }
946
+ get bulkBarClass() {
947
+ return BULK_BAR_CLASSES;
948
+ }
949
+ get resizeHandleClass() {
950
+ return RESIZE_HANDLE_CLASSES;
951
+ }
952
+ get colSpan() {
953
+ return Math.max(this.visibleColumns.length + (this.selectable ? 1 : 0), 1);
954
+ }
955
+ get totalItems() {
956
+ if (this.manualPagination && this.total !== undefined) {
957
+ return this.total;
958
+ }
959
+ return this.data.length;
960
+ }
961
+ get totalPages() {
962
+ return Math.max(1, Math.ceil(this.totalItems / this.currentPageSize));
963
+ }
964
+ get pageLabel() {
965
+ return `Page ${this.currentPage} of ${this.totalPages}`;
966
+ }
967
+ get displayRows() {
968
+ const filtered = this.manualFilter ? this.data : this.getFilteredData(this.data);
969
+ const sorted = this.manualSort ? filtered : this.getSortedData(filtered);
970
+ if (this.manualPagination) {
971
+ return sorted;
972
+ }
973
+ const start = (this.currentPage - 1) * this.currentPageSize;
974
+ return sorted.slice(start, start + this.currentPageSize);
975
+ }
976
+ get visibleColumns() {
977
+ return this.columns.filter((column) => this.columnVisibility[column.key] !== false);
978
+ }
979
+ get selectedCount() {
980
+ return this.selectedKeys.length;
981
+ }
982
+ get allVisibleSelected() {
983
+ if (!this.displayRows.length) {
984
+ return false;
985
+ }
986
+ return this.displayRows.every((row) => this.isSelected(row));
987
+ }
988
+ get someVisibleSelected() {
989
+ return this.displayRows.some((row) => this.isSelected(row)) && !this.allVisibleSelected;
990
+ }
991
+ headerCellClass(column) {
992
+ return [
993
+ HEADER_CELL_BASE_CLASSES$1,
994
+ ALIGN_CLASSES$1[column.align ?? 'left'],
995
+ column.headerClass ?? ''
996
+ ]
997
+ .filter(Boolean)
998
+ .join(' ');
999
+ }
1000
+ headerButtonClass() {
1001
+ return HEADER_BUTTON_CLASSES;
1002
+ }
1003
+ headerFilterInputClass() {
1004
+ return HEADER_FILTER_INPUT_CLASSES;
1005
+ }
1006
+ bodyCellClass(column) {
1007
+ return [
1008
+ BODY_CELL_BASE_CLASSES$1,
1009
+ this.compact ? BODY_CELL_COMPACT_CLASSES$1 : '',
1010
+ ALIGN_CLASSES$1[column.align ?? 'left'],
1011
+ column.cellClass ?? ''
1012
+ ]
1013
+ .filter(Boolean)
1014
+ .join(' ');
1015
+ }
1016
+ cellValue(row, column) {
1017
+ const value = row[column.key];
1018
+ if (column.formatter) {
1019
+ return column.formatter(value, row);
1020
+ }
1021
+ if (value === null || value === undefined || value === '') {
1022
+ return this.emptyCell;
1023
+ }
1024
+ return String(value);
1025
+ }
1026
+ columnWidth(column) {
1027
+ return this.columnWidths[column.key] ?? column.width ?? null;
1028
+ }
1029
+ columnMinWidth(column) {
1030
+ if (column.minWidth) {
1031
+ return `${column.minWidth}px`;
1032
+ }
1033
+ return null;
1034
+ }
1035
+ pinnedStyle(column) {
1036
+ if (!column.pinned) {
1037
+ return null;
1038
+ }
1039
+ if (column.pinned === 'left') {
1040
+ return { left: `${this.pinnedOffsetsLeft[column.key] ?? 0}px` };
1041
+ }
1042
+ return { right: `${this.pinnedOffsetsRight[column.key] ?? 0}px` };
1043
+ }
1044
+ pinnedLeft(column) {
1045
+ const style = this.pinnedStyle(column);
1046
+ return style ? style['left'] ?? null : null;
1047
+ }
1048
+ pinnedRight(column) {
1049
+ const style = this.pinnedStyle(column);
1050
+ return style ? style['right'] ?? null : null;
1051
+ }
1052
+ pinnedClass(column) {
1053
+ if (!column.pinned) {
1054
+ return '';
1055
+ }
1056
+ return 'sticky z-10';
1057
+ }
1058
+ ariaSort(column) {
1059
+ if (!column.sortable) {
1060
+ return null;
1061
+ }
1062
+ if (this.sortKey !== column.key) {
1063
+ return 'none';
1064
+ }
1065
+ return this.sortDirection === 'asc' ? 'ascending' : 'descending';
1066
+ }
1067
+ onSort(column) {
1068
+ if (!column.sortable) {
1069
+ return;
1070
+ }
1071
+ if (this.sortKey === column.key) {
1072
+ this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
1073
+ }
1074
+ else {
1075
+ this.sortKey = column.key;
1076
+ this.sortDirection = 'asc';
1077
+ }
1078
+ this.sortChange.emit({
1079
+ key: this.sortKey,
1080
+ direction: this.sortDirection
1081
+ });
1082
+ }
1083
+ goToPage(page) {
1084
+ const nextPage = Math.min(Math.max(1, page), this.totalPages);
1085
+ this.currentPage = nextPage;
1086
+ this.pageChange.emit(nextPage);
1087
+ }
1088
+ nextPage() {
1089
+ this.goToPage(this.currentPage + 1);
1090
+ }
1091
+ previousPage() {
1092
+ this.goToPage(this.currentPage - 1);
1093
+ }
1094
+ onPageSizeChange(value) {
1095
+ const nextSize = Number(value);
1096
+ if (!Number.isFinite(nextSize) || nextSize <= 0) {
1097
+ return;
1098
+ }
1099
+ this.currentPageSize = nextSize;
1100
+ this.pageSizeChange.emit(nextSize);
1101
+ this.goToPage(1);
1102
+ }
1103
+ onGlobalFilterChange(value) {
1104
+ this.globalFilter = value;
1105
+ this.emitFilterChange();
1106
+ this.goToPage(1);
1107
+ }
1108
+ onColumnFilterChange(columnKey, value) {
1109
+ this.columnFilters[columnKey] = value;
1110
+ this.emitFilterChange();
1111
+ this.goToPage(1);
1112
+ }
1113
+ columnFilterValue(columnKey) {
1114
+ return this.columnFilters[columnKey] || '';
1115
+ }
1116
+ toggleColumnVisibility(columnKey) {
1117
+ this.columnVisibility[columnKey] = !this.columnVisibility[columnKey];
1118
+ this.updatePinnedOffsets();
1119
+ }
1120
+ toggleSelectAll() {
1121
+ if (this.allVisibleSelected) {
1122
+ this.selectedKeys = this.selectedKeys.filter((key) => !this.displayRows.some((row) => this.rowIdentifier(row) === key));
1123
+ }
1124
+ else {
1125
+ const keysToAdd = this.displayRows
1126
+ .map((row) => this.rowIdentifier(row))
1127
+ .filter((key) => !this.selectedKeys.includes(key));
1128
+ this.selectedKeys = [...this.selectedKeys, ...keysToAdd];
1129
+ }
1130
+ this.selectionChange.emit([...this.selectedKeys]);
1131
+ }
1132
+ toggleRowSelection(row) {
1133
+ const key = this.rowIdentifier(row);
1134
+ if (this.selectedKeys.includes(key)) {
1135
+ this.selectedKeys = this.selectedKeys.filter((id) => id !== key);
1136
+ }
1137
+ else {
1138
+ this.selectedKeys = [...this.selectedKeys, key];
1139
+ }
1140
+ this.selectionChange.emit([...this.selectedKeys]);
1141
+ }
1142
+ isSelected(row) {
1143
+ return this.selectedKeys.includes(this.rowIdentifier(row));
1144
+ }
1145
+ runBulkAction(actionId) {
1146
+ this.bulkAction.emit({
1147
+ id: actionId,
1148
+ selected: this.data.filter((row) => this.selectedKeys.includes(this.rowIdentifier(row)))
1149
+ });
1150
+ }
1151
+ onResizeStart(event, column) {
1152
+ if (column.resizable === false) {
1153
+ return;
1154
+ }
1155
+ event.preventDefault();
1156
+ const header = event.target.closest('th');
1157
+ if (!header) {
1158
+ return;
1159
+ }
1160
+ this.isResizing = true;
1161
+ this.resizingColumn = column.key;
1162
+ this.resizeStartX = event.clientX;
1163
+ this.resizeStartWidth = header.getBoundingClientRect().width;
1164
+ const onMove = (moveEvent) => {
1165
+ if (!this.isResizing || !this.resizingColumn) {
1166
+ return;
1167
+ }
1168
+ const delta = moveEvent.clientX - this.resizeStartX;
1169
+ const minWidth = column.minWidth ?? 80;
1170
+ const nextWidth = Math.max(minWidth, this.resizeStartWidth + delta);
1171
+ this.columnWidths[this.resizingColumn] = `${Math.round(nextWidth)}px`;
1172
+ this.updatePinnedOffsets();
1173
+ };
1174
+ const onUp = () => {
1175
+ this.isResizing = false;
1176
+ this.resizingColumn = undefined;
1177
+ window.removeEventListener('mousemove', onMove);
1178
+ window.removeEventListener('mouseup', onUp);
1179
+ };
1180
+ window.addEventListener('mousemove', onMove);
1181
+ window.addEventListener('mouseup', onUp);
1182
+ }
1183
+ getSortedData(rows) {
1184
+ if (!this.sortKey) {
1185
+ return rows;
1186
+ }
1187
+ const direction = this.sortDirection === 'asc' ? 1 : -1;
1188
+ return [...rows].sort((a, b) => {
1189
+ const valueA = a[this.sortKey ?? ''];
1190
+ const valueB = b[this.sortKey ?? ''];
1191
+ const normalizedA = valueA === null || valueA === undefined ? '' : String(valueA);
1192
+ const normalizedB = valueB === null || valueB === undefined ? '' : String(valueB);
1193
+ return normalizedA.localeCompare(normalizedB, undefined, { numeric: true }) * direction;
1194
+ });
1195
+ }
1196
+ getFilteredData(rows) {
1197
+ const global = this.globalFilter.trim().toLowerCase();
1198
+ const filters = this.columnFilters;
1199
+ return rows.filter((row) => {
1200
+ if (global) {
1201
+ const matchesGlobal = this.visibleColumns.some((column) => {
1202
+ const value = row[column.key];
1203
+ return String(value ?? '').toLowerCase().includes(global);
1204
+ });
1205
+ if (!matchesGlobal) {
1206
+ return false;
1207
+ }
1208
+ }
1209
+ return this.visibleColumns.every((column) => {
1210
+ if (!column.filterable) {
1211
+ return true;
1212
+ }
1213
+ const filterValue = (filters[column.key] ?? '').trim().toLowerCase();
1214
+ if (!filterValue) {
1215
+ return true;
1216
+ }
1217
+ const raw = row[column.key];
1218
+ const normalized = String(raw ?? '').toLowerCase();
1219
+ if (column.filterType === 'select') {
1220
+ return normalized === filterValue;
1221
+ }
1222
+ return normalized.includes(filterValue);
1223
+ });
1224
+ });
1225
+ }
1226
+ emitFilterChange() {
1227
+ this.filterChange.emit({
1228
+ global: this.globalFilter,
1229
+ columns: { ...this.columnFilters }
1230
+ });
1231
+ }
1232
+ initializeVisibility() {
1233
+ this.columnVisibility = this.columns.reduce((acc, column) => {
1234
+ acc[column.key] = column.visible !== false;
1235
+ return acc;
1236
+ }, {});
1237
+ }
1238
+ initializeWidths() {
1239
+ this.columnWidths = this.columns.reduce((acc, column) => {
1240
+ if (column.width) {
1241
+ acc[column.key] = column.width;
1242
+ }
1243
+ return acc;
1244
+ }, {});
1245
+ }
1246
+ updatePinnedOffsets() {
1247
+ const leftOffsets = {};
1248
+ const rightOffsets = {};
1249
+ let left = 0;
1250
+ let right = 0;
1251
+ const columns = this.visibleColumns;
1252
+ columns.forEach((column) => {
1253
+ if (column.pinned === 'left') {
1254
+ leftOffsets[column.key] = left;
1255
+ left += this.getWidthValue(column);
1256
+ }
1257
+ });
1258
+ [...columns].reverse().forEach((column) => {
1259
+ if (column.pinned === 'right') {
1260
+ rightOffsets[column.key] = right;
1261
+ right += this.getWidthValue(column);
1262
+ }
1263
+ });
1264
+ this.pinnedOffsetsLeft = leftOffsets;
1265
+ this.pinnedOffsetsRight = rightOffsets;
1266
+ }
1267
+ getWidthValue(column) {
1268
+ const width = this.columnWidths[column.key] ?? column.width;
1269
+ if (!width) {
1270
+ return 160;
1271
+ }
1272
+ const match = width.match(/(\d+)(px)?/);
1273
+ if (match) {
1274
+ return Number(match[1]);
1275
+ }
1276
+ return 160;
1277
+ }
1278
+ rowIdentifier(row) {
1279
+ const value = row[this.rowKey];
1280
+ if (value === undefined || value === null) {
1281
+ return JSON.stringify(row);
1282
+ }
1283
+ return value;
1284
+ }
1285
+ 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 });
1286
+ 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" }, 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 {{ cellValue(row, column) }}\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", changeDetection: i0.ChangeDetectionStrategy.OnPush });
1287
+ }
1288
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsDataTableComponent, decorators: [{
1289
+ type: Component,
1290
+ args: [{ selector: 'cxs-data-table', standalone: true, 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 {{ cellValue(row, column) }}\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" }]
1291
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
1292
+ type: Attribute,
1293
+ args: ['class']
1294
+ }] }], propDecorators: { columns: [{
1295
+ type: Input
1296
+ }], data: [{
1297
+ type: Input
1298
+ }], caption: [{
1299
+ type: Input
1300
+ }], ariaLabel: [{
1301
+ type: Input
1302
+ }], striped: [{
1303
+ type: Input
1304
+ }], bordered: [{
1305
+ type: Input
1306
+ }], compact: [{
1307
+ type: Input
1308
+ }], loading: [{
1309
+ type: Input
1310
+ }], emptyMessage: [{
1311
+ type: Input
1312
+ }], emptyCell: [{
1313
+ type: Input
1314
+ }], pageSize: [{
1315
+ type: Input
1316
+ }], pageIndex: [{
1317
+ type: Input
1318
+ }], pageSizeOptions: [{
1319
+ type: Input
1320
+ }], showPagination: [{
1321
+ type: Input
1322
+ }], showPageSize: [{
1323
+ type: Input
1324
+ }], manualPagination: [{
1325
+ type: Input
1326
+ }], total: [{
1327
+ type: Input
1328
+ }], sortKey: [{
1329
+ type: Input
1330
+ }], sortDirection: [{
1331
+ type: Input
1332
+ }], manualSort: [{
1333
+ type: Input
1334
+ }], showToolbar: [{
1335
+ type: Input
1336
+ }], showFilters: [{
1337
+ type: Input
1338
+ }], showColumnVisibility: [{
1339
+ type: Input
1340
+ }], showGlobalSearch: [{
1341
+ type: Input
1342
+ }], manualFilter: [{
1343
+ type: Input
1344
+ }], selectable: [{
1345
+ type: Input
1346
+ }], rowKey: [{
1347
+ type: Input
1348
+ }], selectedKeys: [{
1349
+ type: Input
1350
+ }], bulkActions: [{
1351
+ type: Input
1352
+ }], pageChange: [{
1353
+ type: Output
1354
+ }], pageSizeChange: [{
1355
+ type: Output
1356
+ }], sortChange: [{
1357
+ type: Output
1358
+ }], filterChange: [{
1359
+ type: Output
1360
+ }], selectionChange: [{
1361
+ type: Output
1362
+ }], bulkAction: [{
1363
+ type: Output
1364
+ }] } });
1365
+
1366
+ const OVERLAY_CLASSES = 'fixed inset-0 z-50 flex items-center justify-center bg-[rgba(15,23,42,0.45)] p-4';
1367
+ const PANEL_BASE_CLASSES = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
1368
+ 'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)] shadow-[var(--cxs-shadow-sm)] ' +
1369
+ 'p-6 outline-none';
1370
+ const PANEL_SIZE_CLASSES = {
1371
+ sm: 'max-w-sm',
1372
+ md: 'max-w-lg',
1373
+ lg: 'max-w-2xl'
1374
+ };
1375
+ const TITLE_CLASSES$1 = 'text-lg font-semibold';
1376
+ const DESCRIPTION_CLASSES = 'mt-1 text-sm text-[var(--cxs-color-on-surface-muted)]';
1377
+ const BODY_CLASSES = 'mt-4 text-sm text-[var(--cxs-color-on-surface)]';
1378
+ const ACTIONS_CLASSES$1 = 'mt-6 flex flex-wrap items-center justify-end gap-2 ' +
1379
+ '[&>*]:flex [&>*]:flex-wrap [&>*]:items-center [&>*]:justify-end [&>*]:gap-2';
1380
+ const CLOSE_BUTTON_CLASSES$1 = 'inline-flex h-8 w-8 items-center justify-center rounded-[var(--cxs-radius-md)] ' +
1381
+ 'text-[var(--cxs-color-on-surface)] transition-colors hover:bg-[var(--cxs-color-surface-hover)] ' +
1382
+ 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
1383
+ 'focus-visible:outline-[var(--cxs-color-focus)]';
1384
+ class CxsDialogComponent {
1385
+ hostClass;
1386
+ static nextId = 0;
1387
+ open = false;
1388
+ title;
1389
+ description;
1390
+ ariaLabel = 'Dialog';
1391
+ size = 'md';
1392
+ dismissible = true;
1393
+ closeOnBackdrop = true;
1394
+ closeOnEscape = true;
1395
+ openChange = new EventEmitter();
1396
+ closed = new EventEmitter();
1397
+ dialogPanel;
1398
+ titleId = `cxs-dialog-title-${CxsDialogComponent.nextId++}`;
1399
+ descriptionId = `cxs-dialog-description-${CxsDialogComponent.nextId++}`;
1400
+ constructor(hostClass) {
1401
+ this.hostClass = hostClass;
1402
+ }
1403
+ ngOnChanges(changes) {
1404
+ if (changes['open']?.currentValue) {
1405
+ this.focusPanelSoon();
1406
+ }
1407
+ }
1408
+ ngAfterViewInit() {
1409
+ if (this.open) {
1410
+ this.focusPanelSoon();
1411
+ }
1412
+ }
1413
+ get overlayClass() {
1414
+ return OVERLAY_CLASSES;
1415
+ }
1416
+ get panelClass() {
1417
+ return [PANEL_BASE_CLASSES, PANEL_SIZE_CLASSES[this.size], this.hostClass]
1418
+ .filter(Boolean)
1419
+ .join(' ');
1420
+ }
1421
+ get titleClass() {
1422
+ return TITLE_CLASSES$1;
1423
+ }
1424
+ get descriptionClass() {
1425
+ return DESCRIPTION_CLASSES;
1426
+ }
1427
+ get bodyClass() {
1428
+ return BODY_CLASSES;
1429
+ }
1430
+ get actionsClass() {
1431
+ return ACTIONS_CLASSES$1;
1432
+ }
1433
+ get closeButtonClass() {
1434
+ return CLOSE_BUTTON_CLASSES$1;
1435
+ }
1436
+ get ariaLabelValue() {
1437
+ if (this.title) {
1438
+ return null;
1439
+ }
1440
+ return this.ariaLabel;
1441
+ }
1442
+ requestClose(reason) {
1443
+ this.openChange.emit(false);
1444
+ this.closed.emit(reason);
1445
+ }
1446
+ onBackdropClick(event) {
1447
+ if (!this.closeOnBackdrop || !this.open) {
1448
+ return;
1449
+ }
1450
+ if (event.target === event.currentTarget) {
1451
+ this.requestClose('backdrop');
1452
+ }
1453
+ }
1454
+ onKeydown(event) {
1455
+ if (!this.closeOnEscape || !this.open) {
1456
+ return;
1457
+ }
1458
+ if (event.key === 'Escape') {
1459
+ event.preventDefault();
1460
+ this.requestClose('escape');
1461
+ }
1462
+ }
1463
+ focusPanelSoon() {
1464
+ queueMicrotask(() => {
1465
+ this.dialogPanel?.nativeElement.focus();
1466
+ });
1467
+ }
1468
+ 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 });
1469
+ 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 });
1470
+ }
1471
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsDialogComponent, decorators: [{
1472
+ type: Component,
1473
+ 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" }]
1474
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
1475
+ type: Attribute,
1476
+ args: ['class']
1477
+ }] }], propDecorators: { open: [{
1478
+ type: Input
1479
+ }], title: [{
1480
+ type: Input
1481
+ }], description: [{
1482
+ type: Input
1483
+ }], ariaLabel: [{
1484
+ type: Input
1485
+ }], size: [{
1486
+ type: Input
1487
+ }], dismissible: [{
1488
+ type: Input
1489
+ }], closeOnBackdrop: [{
1490
+ type: Input
1491
+ }], closeOnEscape: [{
1492
+ type: Input
1493
+ }], openChange: [{
1494
+ type: Output
1495
+ }], closed: [{
1496
+ type: Output
1497
+ }], dialogPanel: [{
1498
+ type: ViewChild,
1499
+ args: ['dialogPanel']
1500
+ }] } });
1501
+
1502
+ const BASE_CLASSES$3 = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
1503
+ 'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)] ' +
1504
+ 'placeholder:text-[var(--cxs-color-on-surface-muted)] transition-colors ' +
1505
+ 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
1506
+ 'focus-visible:outline-[var(--cxs-color-focus)] focus-visible:border-[var(--cxs-color-focus)] ' +
1507
+ 'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-60';
1508
+ const VARIANT_CLASSES$3 = {
1509
+ outline: 'bg-[var(--cxs-color-surface)]',
1510
+ filled: 'bg-[var(--cxs-color-surface-hover)]'
1511
+ };
1512
+ const SIZE_CLASSES$2 = {
1513
+ sm: 'h-8 px-3 text-sm',
1514
+ md: 'h-10 px-3 text-sm',
1515
+ lg: 'h-12 px-4 text-base'
1516
+ };
1517
+ const INVALID_CLASSES$3 = 'border-[var(--cxs-color-danger)] focus-visible:border-[var(--cxs-color-danger)] ' +
1518
+ 'focus-visible:outline-[var(--cxs-color-danger)]';
1519
+ const POPOVER_CLASSES = 'absolute left-0 top-full z-10 mt-2 w-full rounded-[var(--cxs-radius-md)] border ' +
1520
+ 'border-[var(--cxs-color-border)] bg-[var(--cxs-color-surface)] p-3 shadow-[var(--cxs-shadow-sm)]';
1521
+ const NAV_BUTTON_CLASSES = 'inline-flex h-8 w-8 items-center justify-center rounded-[var(--cxs-radius-md)] ' +
1522
+ 'text-[var(--cxs-color-on-surface)] transition-colors hover:bg-[var(--cxs-color-surface-hover)] ' +
1523
+ 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
1524
+ 'focus-visible:outline-[var(--cxs-color-focus)]';
1525
+ const SELECT_CLASSES = 'h-8 rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
1526
+ 'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)] text-xs px-2 ' +
1527
+ 'transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
1528
+ 'focus-visible:outline-[var(--cxs-color-focus)]';
1529
+ const DAY_BUTTON_BASE = 'flex h-8 w-8 items-center justify-center rounded-[var(--cxs-radius-md)] text-xs ' +
1530
+ 'transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
1531
+ 'focus-visible:outline-[var(--cxs-color-focus)] disabled:cursor-not-allowed disabled:opacity-40';
1532
+ const DAY_OUTSIDE_MONTH = 'text-[var(--cxs-color-on-surface-muted)]';
1533
+ const DAY_TODAY = 'border border-[var(--cxs-color-primary)]';
1534
+ const DAY_SELECTED = 'bg-[var(--cxs-color-primary)] text-[var(--cxs-color-on-primary)]';
1535
+ const DAY_HOVER = 'hover:bg-[var(--cxs-color-surface-hover)]';
1536
+ class CxsDatepickerComponent {
1537
+ value = '';
1538
+ variant = 'outline';
1539
+ size = 'md';
1540
+ useNative = true;
1541
+ disabled = false;
1542
+ readonly = false;
1543
+ required = false;
1544
+ invalid = false;
1545
+ autofocus = false;
1546
+ id;
1547
+ name;
1548
+ min;
1549
+ max;
1550
+ placeholder;
1551
+ ariaLabel;
1552
+ ariaDescribedby;
1553
+ valueChange = new EventEmitter();
1554
+ static nextId = 0;
1555
+ fallbackId = `cxs-datepicker-panel-${CxsDatepickerComponent.nextId++}`;
1556
+ disabledFromControl = false;
1557
+ onChange = () => { };
1558
+ onTouched = () => { };
1559
+ isOpen = false;
1560
+ displayMonth = new Date();
1561
+ calendarDays = [];
1562
+ weekDayLabels = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
1563
+ monthOptions = Array.from({ length: 12 }, (_, index) => ({
1564
+ value: index,
1565
+ label: new Date(2020, index, 1).toLocaleString('en-US', { month: 'short' })
1566
+ }));
1567
+ get isDisabled() {
1568
+ return this.disabled || this.disabledFromControl;
1569
+ }
1570
+ get panelId() {
1571
+ return this.id ? `${this.id}-panel` : this.fallbackId;
1572
+ }
1573
+ get inputClass() {
1574
+ return [
1575
+ BASE_CLASSES$3,
1576
+ SIZE_CLASSES$2[this.size],
1577
+ VARIANT_CLASSES$3[this.variant],
1578
+ this.invalid ? INVALID_CLASSES$3 : ''
1579
+ ]
1580
+ .filter(Boolean)
1581
+ .join(' ');
1582
+ }
1583
+ get popoverClass() {
1584
+ return POPOVER_CLASSES;
1585
+ }
1586
+ get navButtonClass() {
1587
+ return NAV_BUTTON_CLASSES;
1588
+ }
1589
+ get selectClass() {
1590
+ return SELECT_CLASSES;
1591
+ }
1592
+ get displayMonthLabel() {
1593
+ return this.displayMonth.toLocaleString('en-US', { month: 'long', year: 'numeric' });
1594
+ }
1595
+ get yearOptions() {
1596
+ const baseYear = this.displayMonth.getFullYear();
1597
+ const minYear = this.parseDate(this.min)?.getFullYear() ?? baseYear - 10;
1598
+ const maxYear = this.parseDate(this.max)?.getFullYear() ?? baseYear + 10;
1599
+ const start = Math.min(minYear, maxYear);
1600
+ const end = Math.max(minYear, maxYear);
1601
+ return Array.from({ length: end - start + 1 }, (_, index) => start + index);
1602
+ }
1603
+ writeValue(value) {
1604
+ this.value = value ?? '';
1605
+ this.syncDisplayMonthWithValue();
1606
+ }
1607
+ registerOnChange(fn) {
1608
+ this.onChange = fn;
1609
+ }
1610
+ registerOnTouched(fn) {
1611
+ this.onTouched = fn;
1612
+ }
1613
+ setDisabledState(isDisabled) {
1614
+ this.disabledFromControl = isDisabled;
1615
+ }
1616
+ onInput(event) {
1617
+ const target = event.target;
1618
+ const nextValue = target.value;
1619
+ this.value = nextValue;
1620
+ this.onChange(nextValue);
1621
+ this.valueChange.emit(nextValue);
1622
+ }
1623
+ onDateSelect(day) {
1624
+ if (day.disabled || this.isDisabled) {
1625
+ return;
1626
+ }
1627
+ const nextValue = this.formatDate(day.date);
1628
+ this.value = nextValue;
1629
+ this.onChange(nextValue);
1630
+ this.valueChange.emit(nextValue);
1631
+ this.isOpen = false;
1632
+ }
1633
+ onBlur() {
1634
+ this.onTouched();
1635
+ }
1636
+ open() {
1637
+ if (this.isDisabled || this.useNative) {
1638
+ return;
1639
+ }
1640
+ this.isOpen = true;
1641
+ this.syncDisplayMonthWithValue();
1642
+ }
1643
+ toggle() {
1644
+ if (this.isOpen) {
1645
+ this.isOpen = false;
1646
+ return;
1647
+ }
1648
+ this.open();
1649
+ }
1650
+ prevMonth() {
1651
+ this.displayMonth = new Date(this.displayMonth.getFullYear(), this.displayMonth.getMonth() - 1, 1);
1652
+ this.updateCalendar();
1653
+ }
1654
+ nextMonth() {
1655
+ this.displayMonth = new Date(this.displayMonth.getFullYear(), this.displayMonth.getMonth() + 1, 1);
1656
+ this.updateCalendar();
1657
+ }
1658
+ onMonthChange(event) {
1659
+ const target = event.target;
1660
+ const nextMonth = Number(target.value);
1661
+ if (Number.isNaN(nextMonth)) {
1662
+ return;
1663
+ }
1664
+ this.displayMonth = new Date(this.displayMonth.getFullYear(), nextMonth, 1);
1665
+ this.updateCalendar();
1666
+ }
1667
+ onYearChange(event) {
1668
+ const target = event.target;
1669
+ const nextYear = Number(target.value);
1670
+ if (Number.isNaN(nextYear)) {
1671
+ return;
1672
+ }
1673
+ this.displayMonth = new Date(nextYear, this.displayMonth.getMonth(), 1);
1674
+ this.updateCalendar();
1675
+ }
1676
+ dayButtonClass(day) {
1677
+ return [
1678
+ DAY_BUTTON_BASE,
1679
+ day.inMonth ? '' : DAY_OUTSIDE_MONTH,
1680
+ day.isToday ? DAY_TODAY : '',
1681
+ this.isSelected(day.date) ? DAY_SELECTED : DAY_HOVER
1682
+ ]
1683
+ .filter(Boolean)
1684
+ .join(' ');
1685
+ }
1686
+ onDocumentClick(event) {
1687
+ if (this.useNative || !this.isOpen) {
1688
+ return;
1689
+ }
1690
+ const target = event.target;
1691
+ if (target && !target.closest(`#${this.panelId}`) && !target.closest(`[data-cxs-datepicker="${this.panelId}"]`)) {
1692
+ this.isOpen = false;
1693
+ }
1694
+ }
1695
+ onEscape() {
1696
+ if (this.useNative || !this.isOpen) {
1697
+ return;
1698
+ }
1699
+ this.isOpen = false;
1700
+ }
1701
+ syncDisplayMonthWithValue() {
1702
+ const selected = this.parseDate(this.value);
1703
+ this.displayMonth = selected ?? new Date();
1704
+ this.updateCalendar();
1705
+ }
1706
+ updateCalendar() {
1707
+ const year = this.displayMonth.getFullYear();
610
1708
  const month = this.displayMonth.getMonth();
611
1709
  const startOfMonth = new Date(year, month, 1);
612
1710
  const startDay = startOfMonth.getDay();
@@ -663,7 +1761,7 @@ class CxsDatepickerComponent {
663
1761
  useExisting: forwardRef(() => CxsDatepickerComponent),
664
1762
  multi: true
665
1763
  }
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 });
1764
+ ], 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
1765
  }
668
1766
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsDatepickerComponent, decorators: [{
669
1767
  type: Component,
@@ -673,7 +1771,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
673
1771
  useExisting: forwardRef(() => CxsDatepickerComponent),
674
1772
  multi: true
675
1773
  }
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" }]
1774
+ ], 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
1775
  }], propDecorators: { value: [{
678
1776
  type: Input
679
1777
  }], variant: [{
@@ -716,13 +1814,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
716
1814
  args: ['document:keydown.escape']
717
1815
  }] } });
718
1816
 
719
- const BASE_CLASSES$1 = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
1817
+ const BASE_CLASSES$2 = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
720
1818
  'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)] ' +
721
1819
  'placeholder:text-[var(--cxs-color-on-surface-muted)] transition-colors ' +
722
1820
  'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
723
1821
  'focus-visible:outline-[var(--cxs-color-focus)] focus-visible:border-[var(--cxs-color-focus)] ' +
724
1822
  'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-60';
725
- const VARIANT_CLASSES$1 = {
1823
+ const VARIANT_CLASSES$2 = {
726
1824
  outline: 'bg-[var(--cxs-color-surface)]',
727
1825
  filled: 'bg-[var(--cxs-color-surface-hover)]'
728
1826
  };
@@ -760,9 +1858,9 @@ class CxsInputComponent {
760
1858
  }
761
1859
  get inputClass() {
762
1860
  return [
763
- BASE_CLASSES$1,
1861
+ BASE_CLASSES$2,
764
1862
  SIZE_CLASSES$1[this.size],
765
- VARIANT_CLASSES$1[this.variant],
1863
+ VARIANT_CLASSES$2[this.variant],
766
1864
  this.invalid ? INVALID_CLASSES$2 : ''
767
1865
  ]
768
1866
  .filter(Boolean)
@@ -787,63 +1885,204 @@ class CxsInputComponent {
787
1885
  this.onChange(nextValue);
788
1886
  this.valueChange.emit(nextValue);
789
1887
  }
790
- onBlur() {
791
- this.onTouched();
1888
+ onBlur() {
1889
+ this.onTouched();
1890
+ }
1891
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1892
+ 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: [
1893
+ {
1894
+ provide: NG_VALUE_ACCESSOR,
1895
+ useExisting: forwardRef(() => CxsInputComponent),
1896
+ multi: true
1897
+ }
1898
+ ], 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 });
1899
+ }
1900
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsInputComponent, decorators: [{
1901
+ type: Component,
1902
+ args: [{ selector: 'cxs-input', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
1903
+ {
1904
+ provide: NG_VALUE_ACCESSOR,
1905
+ useExisting: forwardRef(() => CxsInputComponent),
1906
+ multi: true
1907
+ }
1908
+ ], 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" }]
1909
+ }], propDecorators: { value: [{
1910
+ type: Input
1911
+ }], type: [{
1912
+ type: Input
1913
+ }], variant: [{
1914
+ type: Input
1915
+ }], size: [{
1916
+ type: Input
1917
+ }], disabled: [{
1918
+ type: Input
1919
+ }], readonly: [{
1920
+ type: Input
1921
+ }], required: [{
1922
+ type: Input
1923
+ }], invalid: [{
1924
+ type: Input
1925
+ }], autofocus: [{
1926
+ type: Input
1927
+ }], id: [{
1928
+ type: Input
1929
+ }], name: [{
1930
+ type: Input
1931
+ }], placeholder: [{
1932
+ type: Input
1933
+ }], autocomplete: [{
1934
+ type: Input
1935
+ }], inputmode: [{
1936
+ type: Input
1937
+ }], spellcheck: [{
1938
+ type: Input
1939
+ }], ariaLabel: [{
1940
+ type: Input
1941
+ }], ariaDescribedby: [{
1942
+ type: Input
1943
+ }], valueChange: [{
1944
+ type: Output
1945
+ }] } });
1946
+
1947
+ 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';
1948
+ const BAR_SIZE_CLASSES = {
1949
+ sm: 'text-xs',
1950
+ md: 'text-sm',
1951
+ lg: 'text-base'
1952
+ };
1953
+ const BAR_VARIANT_CLASSES = {
1954
+ default: 'bg-transparent',
1955
+ surface: 'bg-[var(--cxs-color-surface)] border-[var(--cxs-color-border)] shadow-[var(--cxs-shadow-sm)]'
1956
+ };
1957
+ const ITEM_BASE_CLASSES = 'inline-flex items-center gap-2 rounded-[var(--cxs-radius-md)] px-3 py-1.5 font-medium ' +
1958
+ 'transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
1959
+ 'focus-visible:outline-[var(--cxs-color-focus)]';
1960
+ const ITEM_SIZE_CLASSES = {
1961
+ sm: 'px-2.5 py-1 text-xs',
1962
+ md: 'px-3 py-1.5 text-sm',
1963
+ lg: 'px-4 py-2 text-base'
1964
+ };
1965
+ const ITEM_ACTIVE_CLASSES = 'bg-[var(--cxs-color-primary-ghost)] text-[var(--cxs-color-primary)]';
1966
+ const ITEM_INACTIVE_CLASSES = 'text-[var(--cxs-color-on-surface)] hover:bg-[var(--cxs-color-surface-hover)]';
1967
+ const ITEM_DISABLED_CLASSES = 'text-[var(--cxs-color-on-surface-muted)] opacity-60 pointer-events-none';
1968
+ class CxsMenubarComponent {
1969
+ hostClass;
1970
+ items = [];
1971
+ ariaLabel = 'Menubar';
1972
+ size = 'md';
1973
+ variant = 'default';
1974
+ disabled = false;
1975
+ itemSelected = new EventEmitter();
1976
+ menuItems;
1977
+ constructor(hostClass) {
1978
+ this.hostClass = hostClass;
1979
+ }
1980
+ get menubarClass() {
1981
+ return [
1982
+ BAR_BASE_CLASSES,
1983
+ BAR_SIZE_CLASSES[this.size],
1984
+ BAR_VARIANT_CLASSES[this.variant],
1985
+ this.hostClass
1986
+ ]
1987
+ .filter(Boolean)
1988
+ .join(' ');
1989
+ }
1990
+ isItemDisabled(item) {
1991
+ return this.disabled || !!item.disabled;
1992
+ }
1993
+ getItemClass(item) {
1994
+ return [
1995
+ ITEM_BASE_CLASSES,
1996
+ ITEM_SIZE_CLASSES[this.size],
1997
+ item.active ? ITEM_ACTIVE_CLASSES : ITEM_INACTIVE_CLASSES,
1998
+ this.isItemDisabled(item) ? ITEM_DISABLED_CLASSES : ''
1999
+ ]
2000
+ .filter(Boolean)
2001
+ .join(' ');
2002
+ }
2003
+ getItemHref(item) {
2004
+ if (item.href && !this.isItemDisabled(item)) {
2005
+ return item.href;
2006
+ }
2007
+ return null;
2008
+ }
2009
+ getItemRel(item) {
2010
+ if (item.target === '_blank') {
2011
+ return item.rel ?? 'noopener noreferrer';
2012
+ }
2013
+ return item.rel ?? null;
2014
+ }
2015
+ onItemClick(item, event) {
2016
+ if (this.isItemDisabled(item)) {
2017
+ event.preventDefault();
2018
+ event.stopPropagation();
2019
+ return;
2020
+ }
2021
+ this.itemSelected.emit(item);
2022
+ }
2023
+ onKeydown(event) {
2024
+ if (this.disabled) {
2025
+ return;
2026
+ }
2027
+ if (typeof document === 'undefined') {
2028
+ return;
2029
+ }
2030
+ const keys = ['ArrowRight', 'ArrowLeft', 'Home', 'End'];
2031
+ if (!keys.includes(event.key)) {
2032
+ return;
2033
+ }
2034
+ const items = this.getFocusableItems();
2035
+ if (!items.length) {
2036
+ return;
2037
+ }
2038
+ const currentIndex = items.findIndex((item) => item === document.activeElement);
2039
+ let nextIndex = 0;
2040
+ switch (event.key) {
2041
+ case 'Home':
2042
+ nextIndex = 0;
2043
+ break;
2044
+ case 'End':
2045
+ nextIndex = items.length - 1;
2046
+ break;
2047
+ case 'ArrowRight':
2048
+ nextIndex = currentIndex >= 0 ? (currentIndex + 1) % items.length : 0;
2049
+ break;
2050
+ case 'ArrowLeft':
2051
+ nextIndex = currentIndex >= 0 ? (currentIndex - 1 + items.length) % items.length : items.length - 1;
2052
+ break;
2053
+ }
2054
+ items[nextIndex]?.focus();
2055
+ event.preventDefault();
2056
+ }
2057
+ getFocusableItems() {
2058
+ return (this.menuItems?.toArray() ?? [])
2059
+ .map((item) => item.nativeElement)
2060
+ .filter((item) => !item.hasAttribute('data-disabled'));
792
2061
  }
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 });
2062
+ 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 });
2063
+ 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
2064
  }
802
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsInputComponent, decorators: [{
2065
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsMenubarComponent, decorators: [{
803
2066
  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: [{
2067
+ 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" }]
2068
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
2069
+ type: Attribute,
2070
+ args: ['class']
2071
+ }] }], propDecorators: { items: [{
814
2072
  type: Input
815
- }], variant: [{
2073
+ }], ariaLabel: [{
816
2074
  type: Input
817
2075
  }], size: [{
818
2076
  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: [{
2077
+ }], variant: [{
842
2078
  type: Input
843
- }], ariaDescribedby: [{
2079
+ }], disabled: [{
844
2080
  type: Input
845
- }], valueChange: [{
2081
+ }], itemSelected: [{
846
2082
  type: Output
2083
+ }], menuItems: [{
2084
+ type: ViewChildren,
2085
+ args: ['menuItem', { read: ElementRef }]
847
2086
  }] } });
848
2087
 
849
2088
  const WRAPPER_BASE = 'inline-flex items-center gap-2 select-none';
@@ -958,7 +2197,7 @@ class CxsRadioComponent {
958
2197
  useExisting: forwardRef(() => CxsRadioComponent),
959
2198
  multi: true
960
2199
  }
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 });
2200
+ ], 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
2201
  }
963
2202
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsRadioComponent, decorators: [{
964
2203
  type: Component,
@@ -968,7 +2207,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
968
2207
  useExisting: forwardRef(() => CxsRadioComponent),
969
2208
  multi: true
970
2209
  }
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" }]
2210
+ ], 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
2211
  }], propDecorators: { value: [{
973
2212
  type: Input
974
2213
  }], name: [{
@@ -995,13 +2234,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
995
2234
  type: Output
996
2235
  }] } });
997
2236
 
998
- const BASE_CLASSES = 'w-full appearance-none rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
2237
+ const BASE_CLASSES$1 = 'w-full appearance-none rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
999
2238
  'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)] ' +
1000
2239
  'placeholder:text-[var(--cxs-color-on-surface-muted)] transition-colors ' +
1001
2240
  'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
1002
2241
  'focus-visible:outline-[var(--cxs-color-focus)] focus-visible:border-[var(--cxs-color-focus)] ' +
1003
2242
  'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-60';
1004
- const VARIANT_CLASSES = {
2243
+ const VARIANT_CLASSES$1 = {
1005
2244
  outline: 'bg-[var(--cxs-color-surface)]',
1006
2245
  filled: 'bg-[var(--cxs-color-surface-hover)]'
1007
2246
  };
@@ -1034,9 +2273,9 @@ class CxsSelectComponent {
1034
2273
  }
1035
2274
  get selectClass() {
1036
2275
  return [
1037
- BASE_CLASSES,
2276
+ BASE_CLASSES$1,
1038
2277
  SIZE_CLASSES[this.size],
1039
- VARIANT_CLASSES[this.variant],
2278
+ VARIANT_CLASSES$1[this.variant],
1040
2279
  this.invalid ? INVALID_CLASSES : ''
1041
2280
  ]
1042
2281
  .filter(Boolean)
@@ -1077,7 +2316,7 @@ class CxsSelectComponent {
1077
2316
  useExisting: forwardRef(() => CxsSelectComponent),
1078
2317
  multi: true
1079
2318
  }
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 });
2319
+ ], 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
2320
  }
1082
2321
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsSelectComponent, decorators: [{
1083
2322
  type: Component,
@@ -1087,7 +2326,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1087
2326
  useExisting: forwardRef(() => CxsSelectComponent),
1088
2327
  multi: true
1089
2328
  }
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" }]
2329
+ ], 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
2330
  }], propDecorators: { value: [{
1092
2331
  type: Input
1093
2332
  }], variant: [{
@@ -1116,6 +2355,446 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1116
2355
  type: Output
1117
2356
  }] } });
1118
2357
 
2358
+ const WRAPPER_BASE_CLASSES = 'w-full overflow-hidden';
2359
+ const WRAPPER_BORDERED_CLASSES = 'rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)]';
2360
+ const TABLE_BASE_CLASSES = 'w-full border-collapse text-sm text-[var(--cxs-color-on-surface)]';
2361
+ const TABLE_COMPACT_CLASSES = 'text-xs';
2362
+ const HEADER_ROW_CLASSES = 'border-b border-[var(--cxs-color-border)] text-xs uppercase tracking-[0.2em] ' +
2363
+ 'text-[var(--cxs-color-on-surface-muted)]';
2364
+ const HEADER_CELL_BASE_CLASSES = 'px-3 py-2 text-left font-semibold';
2365
+ const BODY_ROW_BASE_CLASSES = 'border-b border-[var(--cxs-color-border)] last:border-b-0';
2366
+ const BODY_ROW_STRIPED_CLASSES = 'odd:bg-[var(--cxs-color-surface)] even:bg-[var(--cxs-color-surface-hover)]';
2367
+ const BODY_CELL_BASE_CLASSES = 'px-3 py-2 align-top';
2368
+ const BODY_CELL_COMPACT_CLASSES = 'px-2 py-1.5';
2369
+ const ALIGN_CLASSES = {
2370
+ left: 'text-left',
2371
+ center: 'text-center',
2372
+ right: 'text-right'
2373
+ };
2374
+ const EMPTY_ROW_CLASSES = 'text-sm text-[var(--cxs-color-on-surface-muted)]';
2375
+ class CxsTableComponent {
2376
+ hostClass;
2377
+ columns = [];
2378
+ data = [];
2379
+ caption;
2380
+ ariaLabel = 'Table';
2381
+ striped = false;
2382
+ bordered = false;
2383
+ compact = false;
2384
+ emptyMessage = 'No data available';
2385
+ emptyCell = '—';
2386
+ constructor(hostClass) {
2387
+ this.hostClass = hostClass;
2388
+ }
2389
+ get wrapperClass() {
2390
+ return [
2391
+ WRAPPER_BASE_CLASSES,
2392
+ this.bordered ? WRAPPER_BORDERED_CLASSES : '',
2393
+ this.hostClass
2394
+ ]
2395
+ .filter(Boolean)
2396
+ .join(' ');
2397
+ }
2398
+ get tableClass() {
2399
+ return [TABLE_BASE_CLASSES, this.compact ? TABLE_COMPACT_CLASSES : '']
2400
+ .filter(Boolean)
2401
+ .join(' ');
2402
+ }
2403
+ get headerRowClass() {
2404
+ return HEADER_ROW_CLASSES;
2405
+ }
2406
+ get bodyRowClass() {
2407
+ return [
2408
+ BODY_ROW_BASE_CLASSES,
2409
+ this.striped ? BODY_ROW_STRIPED_CLASSES : ''
2410
+ ]
2411
+ .filter(Boolean)
2412
+ .join(' ');
2413
+ }
2414
+ get emptyRowClass() {
2415
+ return EMPTY_ROW_CLASSES;
2416
+ }
2417
+ get colSpan() {
2418
+ return Math.max(this.columns.length, 1);
2419
+ }
2420
+ headerCellClass(column) {
2421
+ return [
2422
+ HEADER_CELL_BASE_CLASSES,
2423
+ ALIGN_CLASSES[column.align ?? 'left'],
2424
+ column.headerClass ?? ''
2425
+ ]
2426
+ .filter(Boolean)
2427
+ .join(' ');
2428
+ }
2429
+ bodyCellClass(column) {
2430
+ return [
2431
+ BODY_CELL_BASE_CLASSES,
2432
+ this.compact ? BODY_CELL_COMPACT_CLASSES : '',
2433
+ ALIGN_CLASSES[column.align ?? 'left'],
2434
+ column.cellClass ?? ''
2435
+ ]
2436
+ .filter(Boolean)
2437
+ .join(' ');
2438
+ }
2439
+ cellValue(row, column) {
2440
+ const value = row[column.key];
2441
+ if (value === null || value === undefined || value === '') {
2442
+ return this.emptyCell;
2443
+ }
2444
+ return String(value);
2445
+ }
2446
+ 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 });
2447
+ 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 });
2448
+ }
2449
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsTableComponent, decorators: [{
2450
+ type: Component,
2451
+ 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" }]
2452
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
2453
+ type: Attribute,
2454
+ args: ['class']
2455
+ }] }], propDecorators: { columns: [{
2456
+ type: Input
2457
+ }], data: [{
2458
+ type: Input
2459
+ }], caption: [{
2460
+ type: Input
2461
+ }], ariaLabel: [{
2462
+ type: Input
2463
+ }], striped: [{
2464
+ type: Input
2465
+ }], bordered: [{
2466
+ type: Input
2467
+ }], compact: [{
2468
+ type: Input
2469
+ }], emptyMessage: [{
2470
+ type: Input
2471
+ }], emptyCell: [{
2472
+ type: Input
2473
+ }] } });
2474
+
2475
+ const WRAPPER_CLASSES$1 = 'inline-flex items-center gap-2';
2476
+ const LABEL_BASE_CLASSES = 'text-sm text-[var(--cxs-color-on-surface)] select-none';
2477
+ const LABEL_DISABLED_CLASSES = 'text-[var(--cxs-color-on-surface-muted)]';
2478
+ const TRACK_BASE_CLASSES = 'relative inline-flex items-center rounded-full border transition-colors ' +
2479
+ 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
2480
+ 'focus-visible:outline-[var(--cxs-color-focus)] disabled:pointer-events-none disabled:opacity-60';
2481
+ const TRACK_SIZE_CLASSES = {
2482
+ sm: 'h-5 w-9 p-0.5',
2483
+ md: 'h-6 w-11 p-0.5',
2484
+ lg: 'h-8 w-14 p-0.5'
2485
+ };
2486
+ const TRACK_STATE_CLASSES = {
2487
+ on: 'bg-[var(--cxs-color-primary)] border-[var(--cxs-color-primary)]',
2488
+ off: 'bg-[var(--cxs-color-surface)] border-[var(--cxs-color-border)]'
2489
+ };
2490
+ const THUMB_BASE_CLASSES = 'absolute left-0.5 top-0.5 rounded-full transition-transform duration-200 ease-out';
2491
+ const THUMB_SIZE_CLASSES = {
2492
+ sm: 'h-4 w-4',
2493
+ md: 'h-5 w-5',
2494
+ lg: 'h-7 w-7'
2495
+ };
2496
+ const THUMB_STATE_CLASSES = {
2497
+ on: {
2498
+ sm: 'translate-x-[18px] bg-[var(--cxs-color-on-primary)]',
2499
+ md: 'translate-x-[22px] bg-[var(--cxs-color-on-primary)]',
2500
+ lg: 'translate-x-[26px] bg-[var(--cxs-color-on-primary)]'
2501
+ },
2502
+ off: 'translate-x-0 bg-[var(--cxs-color-surface)]'
2503
+ };
2504
+ class CxsToggleComponent {
2505
+ static nextId = 0;
2506
+ checked = false;
2507
+ disabled = false;
2508
+ size = 'md';
2509
+ label;
2510
+ ariaLabel = 'Toggle';
2511
+ checkedChange = new EventEmitter();
2512
+ labelId = `cxs-toggle-label-${CxsToggleComponent.nextId++}`;
2513
+ get wrapperClass() {
2514
+ return WRAPPER_CLASSES$1;
2515
+ }
2516
+ get labelClass() {
2517
+ return [LABEL_BASE_CLASSES, this.disabled ? LABEL_DISABLED_CLASSES : '']
2518
+ .filter(Boolean)
2519
+ .join(' ');
2520
+ }
2521
+ get trackClass() {
2522
+ return [
2523
+ TRACK_BASE_CLASSES,
2524
+ TRACK_SIZE_CLASSES[this.size],
2525
+ this.checked ? TRACK_STATE_CLASSES.on : TRACK_STATE_CLASSES.off
2526
+ ].join(' ');
2527
+ }
2528
+ get thumbClass() {
2529
+ const thumbState = this.checked
2530
+ ? THUMB_STATE_CLASSES.on[this.size]
2531
+ : THUMB_STATE_CLASSES.off;
2532
+ return [THUMB_BASE_CLASSES, THUMB_SIZE_CLASSES[this.size], thumbState].join(' ');
2533
+ }
2534
+ get ariaLabelValue() {
2535
+ if (this.label) {
2536
+ return null;
2537
+ }
2538
+ return this.ariaLabel;
2539
+ }
2540
+ toggle() {
2541
+ if (this.disabled) {
2542
+ return;
2543
+ }
2544
+ this.checkedChange.emit(!this.checked);
2545
+ }
2546
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsToggleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2547
+ 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 });
2548
+ }
2549
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsToggleComponent, decorators: [{
2550
+ type: Component,
2551
+ 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" }]
2552
+ }], propDecorators: { checked: [{
2553
+ type: Input
2554
+ }], disabled: [{
2555
+ type: Input
2556
+ }], size: [{
2557
+ type: Input
2558
+ }], label: [{
2559
+ type: Input
2560
+ }], ariaLabel: [{
2561
+ type: Input
2562
+ }], checkedChange: [{
2563
+ type: Output
2564
+ }] } });
2565
+
2566
+ const HOST_BASE_CLASSES = 'pointer-events-none';
2567
+ const POSITION_CLASSES = {
2568
+ 'bottom-right': 'fixed bottom-6 right-6 items-end',
2569
+ bottom: 'fixed bottom-6 left-1/2 -translate-x-1/2 items-end',
2570
+ 'bottom-left': 'fixed bottom-6 left-6 items-start',
2571
+ 'top-right': 'fixed top-6 right-6 items-end',
2572
+ top: 'fixed top-6 left-1/2 -translate-x-1/2 items-start',
2573
+ 'top-left': 'fixed top-6 left-6 items-start',
2574
+ right: 'fixed right-6 top-1/2 -translate-y-1/2 items-end',
2575
+ left: 'fixed left-6 top-1/2 -translate-y-1/2 items-start',
2576
+ center: 'fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 items-center'
2577
+ };
2578
+ const BASE_CLASSES = 'pointer-events-auto flex w-full items-start gap-3 rounded-[var(--cxs-radius-md)] ' +
2579
+ 'border px-4 py-3 shadow-[var(--cxs-shadow-sm)]';
2580
+ const WIDTH_CLASSES = {
2581
+ sm: 'max-w-sm',
2582
+ md: 'max-w-md',
2583
+ lg: 'max-w-lg',
2584
+ xl: 'max-w-xl',
2585
+ none: 'max-w-none'
2586
+ };
2587
+ const VARIANT_CLASSES = {
2588
+ info: 'border-[var(--cxs-color-primary)] bg-[var(--cxs-color-primary-ghost)] ' +
2589
+ 'text-[var(--cxs-color-on-surface)]',
2590
+ neutral: 'border-[var(--cxs-color-border)] bg-[var(--cxs-color-surface)] ' +
2591
+ 'text-[var(--cxs-color-on-surface)]',
2592
+ danger: 'border-[var(--cxs-color-danger)] bg-[var(--cxs-color-surface)] ' +
2593
+ 'text-[var(--cxs-color-on-surface)]'
2594
+ };
2595
+ const TITLE_CLASSES = 'text-sm font-semibold';
2596
+ const MESSAGE_CLASSES = 'mt-1 text-sm text-[var(--cxs-color-on-surface-muted)]';
2597
+ const ACTIONS_CLASSES = 'mt-3 flex flex-wrap items-center gap-2 empty:hidden';
2598
+ const CLOSE_BUTTON_CLASSES = 'inline-flex h-8 w-8 items-center justify-center rounded-[var(--cxs-radius-md)] ' +
2599
+ 'text-[var(--cxs-color-on-surface)] transition-colors hover:bg-[var(--cxs-color-surface-hover)] ' +
2600
+ 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
2601
+ 'focus-visible:outline-[var(--cxs-color-focus)]';
2602
+ class CxsToastComponent {
2603
+ hostClass;
2604
+ open = false;
2605
+ title;
2606
+ message;
2607
+ ariaLabel = 'Notification';
2608
+ variant = 'info';
2609
+ dismissible = true;
2610
+ duration = 0;
2611
+ position = 'bottom-right';
2612
+ maxWidth = 'sm';
2613
+ openChange = new EventEmitter();
2614
+ dismissed = new EventEmitter();
2615
+ timeoutId = null;
2616
+ constructor(hostClass) {
2617
+ this.hostClass = hostClass;
2618
+ }
2619
+ ngOnChanges(changes) {
2620
+ if (changes['open'] || changes['duration']) {
2621
+ this.resetTimer();
2622
+ }
2623
+ }
2624
+ ngOnDestroy() {
2625
+ this.clearTimer();
2626
+ }
2627
+ get hostClassName() {
2628
+ return [HOST_BASE_CLASSES, POSITION_CLASSES[this.position]].join(' ');
2629
+ }
2630
+ get toastClass() {
2631
+ return [
2632
+ BASE_CLASSES,
2633
+ WIDTH_CLASSES[this.maxWidth],
2634
+ VARIANT_CLASSES[this.variant],
2635
+ this.hostClass
2636
+ ]
2637
+ .filter(Boolean)
2638
+ .join(' ');
2639
+ }
2640
+ get titleClass() {
2641
+ return TITLE_CLASSES;
2642
+ }
2643
+ get messageClass() {
2644
+ return MESSAGE_CLASSES;
2645
+ }
2646
+ get actionsClass() {
2647
+ return ACTIONS_CLASSES;
2648
+ }
2649
+ get closeButtonClass() {
2650
+ return CLOSE_BUTTON_CLASSES;
2651
+ }
2652
+ get role() {
2653
+ return this.variant === 'danger' ? 'alert' : 'status';
2654
+ }
2655
+ get ariaLive() {
2656
+ return this.variant === 'danger' ? 'assertive' : 'polite';
2657
+ }
2658
+ get ariaLabelValue() {
2659
+ if (this.title) {
2660
+ return null;
2661
+ }
2662
+ return this.ariaLabel;
2663
+ }
2664
+ requestClose(reason) {
2665
+ this.openChange.emit(false);
2666
+ this.dismissed.emit(reason);
2667
+ }
2668
+ resetTimer() {
2669
+ this.clearTimer();
2670
+ if (this.open && this.duration > 0) {
2671
+ this.timeoutId = setTimeout(() => {
2672
+ this.requestClose('timeout');
2673
+ }, this.duration);
2674
+ }
2675
+ }
2676
+ clearTimer() {
2677
+ if (this.timeoutId) {
2678
+ clearTimeout(this.timeoutId);
2679
+ this.timeoutId = null;
2680
+ }
2681
+ }
2682
+ 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 });
2683
+ 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 });
2684
+ }
2685
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsToastComponent, decorators: [{
2686
+ type: Component,
2687
+ 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" }]
2688
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
2689
+ type: Attribute,
2690
+ args: ['class']
2691
+ }] }], propDecorators: { open: [{
2692
+ type: Input
2693
+ }], title: [{
2694
+ type: Input
2695
+ }], message: [{
2696
+ type: Input
2697
+ }], ariaLabel: [{
2698
+ type: Input
2699
+ }], variant: [{
2700
+ type: Input
2701
+ }], dismissible: [{
2702
+ type: Input
2703
+ }], duration: [{
2704
+ type: Input
2705
+ }], position: [{
2706
+ type: Input
2707
+ }], maxWidth: [{
2708
+ type: Input
2709
+ }], openChange: [{
2710
+ type: Output
2711
+ }], dismissed: [{
2712
+ type: Output
2713
+ }] } });
2714
+
2715
+ const WRAPPER_CLASSES = 'relative inline-flex';
2716
+ const TOOLTIP_BASE_CLASSES = 'pointer-events-none absolute z-50 max-w-xs rounded-[var(--cxs-radius-md)] px-2 py-1 text-xs ' +
2717
+ 'text-[var(--cxs-color-on-primary)] bg-[var(--cxs-color-on-surface)] shadow-[var(--cxs-shadow-sm)]';
2718
+ const TOOLTIP_POSITION_CLASSES = {
2719
+ top: 'bottom-full left-1/2 mb-2 -translate-x-1/2',
2720
+ right: 'left-full top-1/2 ml-2 -translate-y-1/2',
2721
+ bottom: 'top-full left-1/2 mt-2 -translate-x-1/2',
2722
+ left: 'right-full top-1/2 mr-2 -translate-y-1/2'
2723
+ };
2724
+ class CxsTooltipComponent {
2725
+ static nextId = 0;
2726
+ text = '';
2727
+ position = 'top';
2728
+ disabled = false;
2729
+ open = false;
2730
+ tooltipId = `cxs-tooltip-${CxsTooltipComponent.nextId++}`;
2731
+ get wrapperClass() {
2732
+ return WRAPPER_CLASSES;
2733
+ }
2734
+ get tooltipClass() {
2735
+ return [TOOLTIP_BASE_CLASSES, TOOLTIP_POSITION_CLASSES[this.position]].join(' ');
2736
+ }
2737
+ get describedBy() {
2738
+ if (!this.text || this.disabled) {
2739
+ return null;
2740
+ }
2741
+ return this.tooltipId;
2742
+ }
2743
+ show() {
2744
+ if (this.disabled || !this.text) {
2745
+ return;
2746
+ }
2747
+ this.open = true;
2748
+ }
2749
+ hide() {
2750
+ this.open = false;
2751
+ }
2752
+ onMouseEnter() {
2753
+ this.show();
2754
+ }
2755
+ onMouseLeave() {
2756
+ this.hide();
2757
+ }
2758
+ onFocusIn() {
2759
+ this.show();
2760
+ }
2761
+ onFocusOut() {
2762
+ this.hide();
2763
+ }
2764
+ onKeydown(event) {
2765
+ if (event.key === 'Escape') {
2766
+ this.hide();
2767
+ }
2768
+ }
2769
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsTooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2770
+ 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 });
2771
+ }
2772
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CxsTooltipComponent, decorators: [{
2773
+ type: Component,
2774
+ 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" }]
2775
+ }], propDecorators: { text: [{
2776
+ type: Input
2777
+ }], position: [{
2778
+ type: Input
2779
+ }], disabled: [{
2780
+ type: Input
2781
+ }], onMouseEnter: [{
2782
+ type: HostListener,
2783
+ args: ['mouseenter']
2784
+ }], onMouseLeave: [{
2785
+ type: HostListener,
2786
+ args: ['mouseleave']
2787
+ }], onFocusIn: [{
2788
+ type: HostListener,
2789
+ args: ['focusin']
2790
+ }], onFocusOut: [{
2791
+ type: HostListener,
2792
+ args: ['focusout']
2793
+ }], onKeydown: [{
2794
+ type: HostListener,
2795
+ args: ['keydown', ['$event']]
2796
+ }] } });
2797
+
1119
2798
  const DEFAULT_DARK_CLASS = 'cxs-theme-dark';
1120
2799
  class CxsThemeDirective {
1121
2800
  host;
@@ -1223,5 +2902,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
1223
2902
  * Generated bundle index. Do not edit.
1224
2903
  */
1225
2904
 
1226
- export { CerxosUi, CxsAlertComponent, CxsBadgeComponent, CxsBreadcrumbComponent, CxsButtonComponent, CxsCardComponent, CxsCheckboxComponent, CxsDatepickerComponent, CxsInputComponent, CxsRadioComponent, CxsSelectComponent, CxsThemeDirective };
2905
+ export { CerxosUi, CxsAccordionComponent, CxsAccordionItemComponent, CxsAlertComponent, CxsAvatarComponent, CxsBadgeComponent, CxsBreadcrumbComponent, CxsButtonComponent, CxsCardComponent, CxsCarouselComponent, CxsCheckboxComponent, CxsDataTableComponent, CxsDatepickerComponent, CxsDialogComponent, CxsInputComponent, CxsMenubarComponent, CxsRadioComponent, CxsSelectComponent, CxsTableComponent, CxsThemeDirective, CxsToastComponent, CxsToggleComponent, CxsTooltipComponent };
1227
2906
  //# sourceMappingURL=cerxos-ui.mjs.map