@caribeng/ui 22.2.0 → 22.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/README.md +93 -11
  2. package/fesm2022/caribeng-ui-accordion.mjs +2 -2
  3. package/fesm2022/caribeng-ui-avatar.mjs +2 -2
  4. package/fesm2022/caribeng-ui-badge.mjs +8 -2
  5. package/fesm2022/caribeng-ui-breadcrumb.mjs +13 -6
  6. package/fesm2022/caribeng-ui-chart.mjs +8 -5
  7. package/fesm2022/caribeng-ui-chat.mjs +1303 -0
  8. package/fesm2022/caribeng-ui-code-block.mjs +332 -0
  9. package/fesm2022/caribeng-ui-colorpicker.mjs +99 -5
  10. package/fesm2022/caribeng-ui-command-palette.mjs +476 -0
  11. package/fesm2022/caribeng-ui-confirmdialog.mjs +78 -13
  12. package/fesm2022/caribeng-ui-confirmpopup.mjs +91 -16
  13. package/fesm2022/caribeng-ui-contextmenu.mjs +8 -4
  14. package/fesm2022/caribeng-ui-copy-button.mjs +230 -0
  15. package/fesm2022/caribeng-ui-dataview.mjs +4 -1
  16. package/fesm2022/caribeng-ui-dock.mjs +52 -12
  17. package/fesm2022/caribeng-ui-drawer.mjs +9 -4
  18. package/fesm2022/caribeng-ui-editor.mjs +21 -0
  19. package/fesm2022/caribeng-ui-fieldset.mjs +2 -2
  20. package/fesm2022/caribeng-ui-file-card.mjs +134 -17
  21. package/fesm2022/caribeng-ui-folder-card.mjs +122 -12
  22. package/fesm2022/caribeng-ui-inlinemessage.mjs +2 -2
  23. package/fesm2022/caribeng-ui-inputmask.mjs +8 -2
  24. package/fesm2022/caribeng-ui-kanban.mjs +4612 -0
  25. package/fesm2022/caribeng-ui-kbd.mjs +122 -0
  26. package/fesm2022/caribeng-ui-megamenu.mjs +13 -9
  27. package/fesm2022/caribeng-ui-mentions.mjs +495 -0
  28. package/fesm2022/caribeng-ui-menu.mjs +8 -4
  29. package/fesm2022/caribeng-ui-menubar.mjs +9 -5
  30. package/fesm2022/caribeng-ui-message.mjs +6 -3
  31. package/fesm2022/caribeng-ui-organizationchart.mjs +6 -10
  32. package/fesm2022/caribeng-ui-panel.mjs +6 -6
  33. package/fesm2022/caribeng-ui-panelmenu.mjs +9 -6
  34. package/fesm2022/caribeng-ui-phone-input.mjs +688 -0
  35. package/fesm2022/caribeng-ui-pricing.mjs +263 -69
  36. package/fesm2022/caribeng-ui-qrcode.mjs +564 -0
  37. package/fesm2022/caribeng-ui-ripple.mjs +2 -0
  38. package/fesm2022/caribeng-ui-scrolltop.mjs +3 -1
  39. package/fesm2022/caribeng-ui-sidebar.mjs +573 -0
  40. package/fesm2022/caribeng-ui-stepper.mjs +10 -3
  41. package/fesm2022/caribeng-ui-table.mjs +10 -4
  42. package/fesm2022/caribeng-ui-tabmenu.mjs +8 -4
  43. package/fesm2022/caribeng-ui-tieredmenu.mjs +109 -19
  44. package/fesm2022/caribeng-ui.mjs +388 -1
  45. package/package.json +42 -2
  46. package/schematics/collection.json +5 -0
  47. package/schematics/migrate-from-primeng/core-icons.json +1 -0
  48. package/schematics/migrate-from-primeng/icons.js +269 -0
  49. package/schematics/migrate-from-primeng/index.js +290 -0
  50. package/schematics/migrate-from-primeng/mappings/button.js +93 -0
  51. package/schematics/migrate-from-primeng/mappings/card.js +33 -0
  52. package/schematics/migrate-from-primeng/mappings/checkbox.js +44 -0
  53. package/schematics/migrate-from-primeng/mappings/confirm.js +221 -0
  54. package/schematics/migrate-from-primeng/mappings/datepicker.js +121 -0
  55. package/schematics/migrate-from-primeng/mappings/dialog.js +84 -0
  56. package/schematics/migrate-from-primeng/mappings/index.js +88 -0
  57. package/schematics/migrate-from-primeng/mappings/inputnumber.js +62 -0
  58. package/schematics/migrate-from-primeng/mappings/inputtext.js +60 -0
  59. package/schematics/migrate-from-primeng/mappings/menu.js +468 -0
  60. package/schematics/migrate-from-primeng/mappings/multiselect.js +49 -0
  61. package/schematics/migrate-from-primeng/mappings/radiobutton.js +22 -0
  62. package/schematics/migrate-from-primeng/mappings/rules.js +206 -0
  63. package/schematics/migrate-from-primeng/mappings/select.js +97 -0
  64. package/schematics/migrate-from-primeng/mappings/service-calls.js +100 -0
  65. package/schematics/migrate-from-primeng/mappings/table.js +613 -0
  66. package/schematics/migrate-from-primeng/mappings/tabs.js +225 -0
  67. package/schematics/migrate-from-primeng/mappings/tag.js +29 -0
  68. package/schematics/migrate-from-primeng/mappings/textarea.js +44 -0
  69. package/schematics/migrate-from-primeng/mappings/toast.js +173 -0
  70. package/schematics/migrate-from-primeng/mappings/toggleswitch.js +37 -0
  71. package/schematics/migrate-from-primeng/mappings/tooltip.js +44 -0
  72. package/schematics/migrate-from-primeng/mappings/types.js +10 -0
  73. package/schematics/migrate-from-primeng/report.js +79 -0
  74. package/schematics/migrate-from-primeng/schema.js +2 -0
  75. package/schematics/migrate-from-primeng/schema.json +34 -0
  76. package/schematics/migrate-from-primeng/template.js +387 -0
  77. package/schematics/migrate-from-primeng/todo.js +5 -0
  78. package/schematics/migrate-from-primeng/typescript.js +442 -0
  79. package/schematics/migrate-from-primeng/workspace.js +117 -0
  80. package/schematics/ng-add/index.js +16 -1
  81. package/types/caribeng-ui-badge.d.ts +3 -1
  82. package/types/caribeng-ui-breadcrumb.d.ts +8 -0
  83. package/types/caribeng-ui-chart.d.ts +3 -1
  84. package/types/caribeng-ui-chat.d.ts +299 -0
  85. package/types/caribeng-ui-code-block.d.ts +65 -0
  86. package/types/caribeng-ui-colorpicker.d.ts +5 -1
  87. package/types/caribeng-ui-command-palette.d.ts +133 -0
  88. package/types/caribeng-ui-confirmdialog.d.ts +34 -2
  89. package/types/caribeng-ui-confirmpopup.d.ts +39 -2
  90. package/types/caribeng-ui-contextmenu.d.ts +7 -0
  91. package/types/caribeng-ui-copy-button.d.ts +90 -0
  92. package/types/caribeng-ui-dataview.d.ts +2 -0
  93. package/types/caribeng-ui-dock.d.ts +13 -1
  94. package/types/caribeng-ui-drawer.d.ts +3 -1
  95. package/types/caribeng-ui-file-card.d.ts +27 -2
  96. package/types/caribeng-ui-folder-card.d.ts +27 -5
  97. package/types/caribeng-ui-inputmask.d.ts +3 -1
  98. package/types/caribeng-ui-kanban.d.ts +783 -0
  99. package/types/caribeng-ui-kbd.d.ts +36 -0
  100. package/types/caribeng-ui-megamenu.d.ts +11 -0
  101. package/types/caribeng-ui-mentions.d.ts +127 -0
  102. package/types/caribeng-ui-menu.d.ts +7 -0
  103. package/types/caribeng-ui-menubar.d.ts +7 -0
  104. package/types/caribeng-ui-message.d.ts +2 -0
  105. package/types/caribeng-ui-panelmenu.d.ts +7 -0
  106. package/types/caribeng-ui-phone-input.d.ts +168 -0
  107. package/types/caribeng-ui-pricing.d.ts +41 -4
  108. package/types/caribeng-ui-qrcode.d.ts +88 -0
  109. package/types/caribeng-ui-sidebar.d.ts +167 -0
  110. package/types/caribeng-ui-stepper.d.ts +2 -0
  111. package/types/caribeng-ui-table.d.ts +1 -1
  112. package/types/caribeng-ui-tabmenu.d.ts +7 -0
  113. package/types/caribeng-ui-tieredmenu.d.ts +17 -1
  114. package/types/caribeng-ui.d.ts +276 -2
  115. package/styles/caribeng.css +0 -380
  116. package/styles/scss/_mixins.scss +0 -46
  117. package/styles/scss/_overlay.scss +0 -15
  118. package/styles/scss/_tokens.scss +0 -267
  119. package/styles/scss/_utilities.scss +0 -73
  120. package/styles/scss/_variables.scss +0 -33
  121. package/styles/scss/index.scss +0 -16
@@ -1,9 +1,9 @@
1
1
  import { NgTemplateOutlet } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { inject, ViewContainerRef, Injector, model, input, output, viewChild, signal, afterNextRender, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { inject, ViewContainerRef, Injector, model, input, output, viewChild, signal, computed, effect, untracked, afterNextRender, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
4
4
  import { Overlay } from '@angular/cdk/overlay';
5
5
  import { TemplatePortal } from '@angular/cdk/portal';
6
- import { injectCbTranslation } from '@caribeng/ui';
6
+ import { injectCbTranslation, CbConfirmationService } from '@caribeng/ui';
7
7
  import { CbButton } from '@caribeng/ui/button';
8
8
 
9
9
  let nextUid = 0;
@@ -17,6 +17,19 @@ let nextUid = 0;
17
17
  * <cb-button severity="danger" (click)="borrar.confirm($event)">Eliminar</cb-button>
18
18
  * <cb-confirm-popup #borrar message="¿Eliminar este archivo?" (accepted)="eliminar()" />
19
19
  * ```
20
+ *
21
+ * Con `service` muestra las confirmaciones de `CbConfirmationService` que traen
22
+ * `target` (como `ConfirmationService` de PrimeNG):
23
+ *
24
+ * ```html
25
+ * <cb-button (click)="borrar($event)">Eliminar</cb-button>
26
+ * <cb-confirm-popup service />
27
+ * ```
28
+ * ```ts
29
+ * borrar(event: Event) {
30
+ * inject(CbConfirmationService).confirm({ target: event.currentTarget, message: '¿Eliminar?', accept: () => this.eliminar() });
31
+ * }
32
+ * ```
20
33
  */
21
34
  class CbConfirmPopup {
22
35
  constructor() {
@@ -24,6 +37,7 @@ class CbConfirmPopup {
24
37
  this.overlay = inject(Overlay);
25
38
  this.vcr = inject(ViewContainerRef);
26
39
  this.injector = inject(Injector);
40
+ this.confirmation = inject(CbConfirmationService);
27
41
  this.messageId = `cb-confirm-popup-${++nextUid}-message`;
28
42
  /** Visibilidad (two-way). Con `[(visible)]` se muestra en línea. */
29
43
  this.visible = model(false, /* @ts-ignore */
@@ -38,6 +52,15 @@ class CbConfirmPopup {
38
52
  /** Severidad del botón de aceptar (p. ej. `danger` para acciones destructivas). */
39
53
  this.acceptSeverity = input('primary', /* @ts-ignore */
40
54
  ...(ngDevMode ? [{ debugName: "acceptSeverity" }] : /* istanbul ignore next */ []));
55
+ /**
56
+ * Muestra las confirmaciones pedidas con `CbConfirmationService`. Sin él, el
57
+ * componente solo responde a `[(visible)]` (así un diálogo declarativo no se
58
+ * abre con las confirmaciones de otra parte de la app).
59
+ */
60
+ this.service = input(false, { ...(ngDevMode ? { debugName: "service" } : /* istanbul ignore next */ {}), transform: toBoolean });
61
+ /** Con `service`: atiende solo las confirmaciones con este mismo `key` (o sin key si no se indica). */
62
+ this.key = input(undefined, /* @ts-ignore */
63
+ ...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
41
64
  this.accepted = output();
42
65
  this.rejected = output();
43
66
  this.panel = viewChild.required('panel');
@@ -46,10 +69,45 @@ class CbConfirmPopup {
46
69
  ...(ngDevMode ? [{ debugName: "anchored" }] : /* istanbul ignore next */ []));
47
70
  this.overlayRef = null;
48
71
  this.trigger = null;
72
+ /** Confirmación pedida con `CbConfirmationService` que se está mostrando. */
73
+ this.active = signal(null, /* @ts-ignore */
74
+ ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
75
+ /** Textos visibles: los de la confirmación del servicio, si la hay; si no, los inputs y el idioma. */
76
+ this.shown = computed(() => {
77
+ const active = this.active();
78
+ const t = this.t();
79
+ return {
80
+ message: active?.message ?? this.message() ?? t.confirmMessage,
81
+ acceptLabel: active?.acceptLabel ?? this.acceptLabel() ?? t.accept,
82
+ rejectLabel: active?.rejectLabel ?? this.rejectLabel() ?? t.cancel,
83
+ acceptSeverity: active?.acceptSeverity ?? this.acceptSeverity(),
84
+ };
85
+ }, /* @ts-ignore */
86
+ ...(ngDevMode ? [{ debugName: "shown" }] : /* istanbul ignore next */ []));
49
87
  this.positions = [
50
88
  { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 8 },
51
89
  { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -8 },
52
90
  ];
91
+ effect(() => {
92
+ if (!this.service())
93
+ return;
94
+ const request = this.confirmation.request();
95
+ untracked(() => {
96
+ const target = request?.confirmation.target;
97
+ if (request && request.confirmation.key === this.key() && target instanceof HTMLElement) {
98
+ // Una nueva petición sustituye a la que estuviera abierta.
99
+ if (this.overlayRef)
100
+ this.dispose();
101
+ this.active.set(request.confirmation);
102
+ this.show(target);
103
+ }
104
+ else if (!request && this.active()) {
105
+ // Cerrada desde el servicio (close()).
106
+ this.active.set(null);
107
+ this.close();
108
+ }
109
+ });
110
+ });
53
111
  }
54
112
  /** Abre la confirmación anclada al elemento que recibió el evento. */
55
113
  confirm(event) {
@@ -77,24 +135,38 @@ class CbConfirmPopup {
77
135
  }
78
136
  accept() {
79
137
  this.accepted.emit();
80
- this.close();
138
+ this.finish('accept');
81
139
  }
82
140
  reject() {
83
141
  this.rejected.emit();
142
+ this.finish('reject');
143
+ }
144
+ /** Cierra y, si venía del servicio, lo libera y ejecuta su callback (que puede pedir otra confirmación). */
145
+ finish(action) {
146
+ const active = this.active();
147
+ this.active.set(null);
84
148
  this.close();
149
+ if (active) {
150
+ this.confirmation.close();
151
+ active[action]?.();
152
+ }
85
153
  }
86
154
  close() {
87
155
  this.visible.set(false);
88
156
  if (this.overlayRef) {
89
- this.overlayRef.dispose();
90
- this.overlayRef = null;
91
- this.anchored.set(false);
157
+ this.dispose();
92
158
  this.trigger?.focus();
93
159
  this.trigger = null;
94
160
  }
95
161
  }
162
+ /** Quita el overlay sin devolver el foco. */
163
+ dispose() {
164
+ this.overlayRef?.dispose();
165
+ this.overlayRef = null;
166
+ this.anchored.set(false);
167
+ }
96
168
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbConfirmPopup, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
97
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbConfirmPopup, isStandalone: true, selector: "cb-confirm-popup", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, acceptLabel: { classPropertyName: "acceptLabel", publicName: "acceptLabel", isSignal: true, isRequired: false, transformFunction: null }, rejectLabel: { classPropertyName: "rejectLabel", publicName: "rejectLabel", isSignal: true, isRequired: false, transformFunction: null }, acceptSeverity: { classPropertyName: "acceptSeverity", publicName: "acceptSeverity", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { visible: "visibleChange", accepted: "accepted", rejected: "rejected" }, host: { classAttribute: "cb-confirm-popup-host" }, viewQueries: [{ propertyName: "panel", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: `
169
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbConfirmPopup, isStandalone: true, selector: "cb-confirm-popup", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, acceptLabel: { classPropertyName: "acceptLabel", publicName: "acceptLabel", isSignal: true, isRequired: false, transformFunction: null }, rejectLabel: { classPropertyName: "rejectLabel", publicName: "rejectLabel", isSignal: true, isRequired: false, transformFunction: null }, acceptSeverity: { classPropertyName: "acceptSeverity", publicName: "acceptSeverity", isSignal: true, isRequired: false, transformFunction: null }, service: { classPropertyName: "service", publicName: "service", isSignal: true, isRequired: false, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { visible: "visibleChange", accepted: "accepted", rejected: "rejected" }, host: { classAttribute: "cb-confirm-popup-host" }, viewQueries: [{ propertyName: "panel", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: `
98
170
  @if (visible() && !anchored()) {
99
171
  <ng-container *ngTemplateOutlet="panel" />
100
172
  }
@@ -107,13 +179,13 @@ class CbConfirmPopup {
107
179
  [attr.aria-describedby]="messageId"
108
180
  (keydown.escape)="reject()"
109
181
  >
110
- <p class="cb-confirm-popup__message" [id]="messageId">{{ message() ?? t().confirmMessage }}</p>
182
+ <p class="cb-confirm-popup__message" [id]="messageId">{{ shown().message }}</p>
111
183
  <div class="cb-confirm-popup__actions">
112
184
  <cb-button size="sm" variant="text" severity="secondary" (click)="reject()">
113
- {{ rejectLabel() ?? t().cancel }}
185
+ {{ shown().rejectLabel }}
114
186
  </cb-button>
115
- <cb-button size="sm" [severity]="acceptSeverity()" (click)="accept()">
116
- {{ acceptLabel() ?? t().accept }}
187
+ <cb-button size="sm" [severity]="shown().acceptSeverity" (click)="accept()">
188
+ {{ shown().acceptLabel }}
117
189
  </cb-button>
118
190
  </div>
119
191
  </section>
@@ -135,19 +207,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
135
207
  [attr.aria-describedby]="messageId"
136
208
  (keydown.escape)="reject()"
137
209
  >
138
- <p class="cb-confirm-popup__message" [id]="messageId">{{ message() ?? t().confirmMessage }}</p>
210
+ <p class="cb-confirm-popup__message" [id]="messageId">{{ shown().message }}</p>
139
211
  <div class="cb-confirm-popup__actions">
140
212
  <cb-button size="sm" variant="text" severity="secondary" (click)="reject()">
141
- {{ rejectLabel() ?? t().cancel }}
213
+ {{ shown().rejectLabel }}
142
214
  </cb-button>
143
- <cb-button size="sm" [severity]="acceptSeverity()" (click)="accept()">
144
- {{ acceptLabel() ?? t().accept }}
215
+ <cb-button size="sm" [severity]="shown().acceptSeverity" (click)="accept()">
216
+ {{ shown().acceptLabel }}
145
217
  </cb-button>
146
218
  </div>
147
219
  </section>
148
220
  </ng-template>
149
221
  `, styles: [":host{display:inline-block}.cb-confirm-popup{min-width:15rem;max-width:22rem;padding:var(--cb-space-4, 1rem);border:1px solid var(--cb-color-border);border-radius:var(--cb-radius-lg, .75rem);background:var(--cb-color-surface-raised);color:var(--cb-color-text);box-shadow:0 .75rem 2rem color-mix(in srgb,#000 18%,transparent)}.cb-confirm-popup__message{margin:0 0 var(--cb-space-3, .75rem);font-size:var(--cb-text-sm, .875rem);line-height:1.5}.cb-confirm-popup__actions{display:flex;justify-content:flex-end;gap:var(--cb-space-2, .5rem)}\n"] }]
150
- }], propDecorators: { visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }, { type: i0.Output, args: ["visibleChange"] }], message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: false }] }], acceptLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "acceptLabel", required: false }] }], rejectLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "rejectLabel", required: false }] }], acceptSeverity: [{ type: i0.Input, args: [{ isSignal: true, alias: "acceptSeverity", required: false }] }], accepted: [{ type: i0.Output, args: ["accepted"] }], rejected: [{ type: i0.Output, args: ["rejected"] }], panel: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }] } });
222
+ }], ctorParameters: () => [], propDecorators: { visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }, { type: i0.Output, args: ["visibleChange"] }], message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: false }] }], acceptLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "acceptLabel", required: false }] }], rejectLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "rejectLabel", required: false }] }], acceptSeverity: [{ type: i0.Input, args: [{ isSignal: true, alias: "acceptSeverity", required: false }] }], service: [{ type: i0.Input, args: [{ isSignal: true, alias: "service", required: false }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: false }] }], accepted: [{ type: i0.Output, args: ["accepted"] }], rejected: [{ type: i0.Output, args: ["rejected"] }], panel: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }] } });
223
+ function toBoolean(value) {
224
+ return value !== false && value != null && `${value}` !== 'false';
225
+ }
151
226
 
152
227
  /**
153
228
  * Generated bundle index. Do not edit.
@@ -1,9 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, ViewContainerRef, DestroyRef, input, output, viewChild, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
2
+ import { inject, ViewContainerRef, DestroyRef, input, computed, output, viewChild, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
3
3
  import { Overlay } from '@angular/cdk/overlay';
4
4
  import { TemplatePortal } from '@angular/cdk/portal';
5
5
  import { CbIcon } from '@caribeng/ui/icon';
6
- import { injectCbTranslation } from '@caribeng/ui';
6
+ import { injectCbTranslation, cbVisibleItems, cbRunCommand } from '@caribeng/ui';
7
7
 
8
8
  /** Context menu that opens at the pointer position on right click. */
9
9
  class CbContextMenu {
@@ -15,6 +15,9 @@ class CbContextMenu {
15
15
  this.destroyRef = inject(DestroyRef);
16
16
  this.items = input([], /* @ts-ignore */
17
17
  ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
18
+ /** Items sin los de `visible: false`. */
19
+ this.visibleItems = computed(() => cbVisibleItems(this.items()), /* @ts-ignore */
20
+ ...(ngDevMode ? [{ debugName: "visibleItems" }] : /* istanbul ignore next */ []));
18
21
  this.ariaLabel = input(undefined, /* @ts-ignore */
19
22
  ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
20
23
  this.itemSelect = output();
@@ -58,6 +61,7 @@ class CbContextMenu {
58
61
  select(item) {
59
62
  if (item.disabled || item.separator)
60
63
  return;
64
+ cbRunCommand(item);
61
65
  this.itemSelect.emit(item);
62
66
  this.close();
63
67
  }
@@ -70,7 +74,7 @@ class CbContextMenu {
70
74
  <ng-content />
71
75
  <ng-template #content>
72
76
  <div class="cb-contextmenu__panel" role="menu" [attr.aria-label]="(ariaLabel() ?? t().contextMenu)">
73
- @for (item of items(); track $index) {
77
+ @for (item of visibleItems(); track $index) {
74
78
  @if (item.separator) { <div class="cb-contextmenu__separator" role="separator"></div> }
75
79
  @else {
76
80
  <button class="cb-contextmenu__item" type="button" role="menuitem" [disabled]="item.disabled" (click)="select(item)">
@@ -89,7 +93,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
89
93
  <ng-content />
90
94
  <ng-template #content>
91
95
  <div class="cb-contextmenu__panel" role="menu" [attr.aria-label]="(ariaLabel() ?? t().contextMenu)">
92
- @for (item of items(); track $index) {
96
+ @for (item of visibleItems(); track $index) {
93
97
  @if (item.separator) { <div class="cb-contextmenu__separator" role="separator"></div> }
94
98
  @else {
95
99
  <button class="cb-contextmenu__item" type="button" role="menuitem" [disabled]="item.disabled" (click)="select(item)">
@@ -0,0 +1,230 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, Injectable, input, booleanAttribute, output, signal, computed, DestroyRef, ViewEncapsulation, ChangeDetectionStrategy, Component, Directive } from '@angular/core';
3
+ import { injectCbTranslation } from '@caribeng/ui';
4
+ import { CbButton } from '@caribeng/ui/button';
5
+ import { CbIcon } from '@caribeng/ui/icon';
6
+ import { DOCUMENT } from '@angular/common';
7
+
8
+ /**
9
+ * Portapapeles de CaribeNG. Usa la API asíncrona del navegador y, si no está
10
+ * disponible (HTTP sin contexto seguro, navegadores antiguos), un textarea
11
+ * temporal con `execCommand('copy')`.
12
+ */
13
+ class CbClipboard {
14
+ constructor() {
15
+ this.doc = inject(DOCUMENT);
16
+ }
17
+ /** Copia `text`. Resuelve `true` si se copió y `false` si el navegador lo impidió. */
18
+ async copy(text) {
19
+ const clipboard = this.doc.defaultView?.navigator?.clipboard;
20
+ if (clipboard?.writeText) {
21
+ try {
22
+ await clipboard.writeText(text);
23
+ return true;
24
+ }
25
+ catch {
26
+ // Sin permiso o sin foco: se intenta el método clásico.
27
+ }
28
+ }
29
+ return this.copyWithTextarea(text);
30
+ }
31
+ copyWithTextarea(text) {
32
+ const doc = this.doc;
33
+ if (!doc.body || typeof doc.execCommand !== 'function')
34
+ return false;
35
+ const active = doc.activeElement;
36
+ const area = doc.createElement('textarea');
37
+ area.value = text;
38
+ area.setAttribute('readonly', '');
39
+ area.style.position = 'fixed';
40
+ area.style.opacity = '0';
41
+ area.style.pointerEvents = 'none';
42
+ doc.body.appendChild(area);
43
+ area.select();
44
+ let ok = false;
45
+ try {
46
+ ok = doc.execCommand('copy');
47
+ }
48
+ catch {
49
+ ok = false;
50
+ }
51
+ area.remove();
52
+ active?.focus?.();
53
+ return ok;
54
+ }
55
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbClipboard, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
56
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbClipboard, providedIn: 'root' }); }
57
+ }
58
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbClipboard, decorators: [{
59
+ type: Injectable,
60
+ args: [{ providedIn: 'root' }]
61
+ }] });
62
+
63
+ /**
64
+ * Botón que copia un texto al portapapeles y confirma con un ✓ durante unos
65
+ * segundos. El resultado se anuncia a los lectores de pantalla.
66
+ *
67
+ * @example
68
+ * ```html
69
+ * <span>0012 3456 7890</span> <cb-copy-button value="001234567890" />
70
+ * <cb-copy-button [value]="apiKey" showLabel variant="outlined" />
71
+ * ```
72
+ */
73
+ class CbCopyButton {
74
+ constructor() {
75
+ /** Textos de interfaz en el idioma activo (`provideCaribeNG({ locale })`). */
76
+ this.t = injectCbTranslation();
77
+ this.clipboard = inject(CbClipboard);
78
+ this.timer = null;
79
+ /** Texto que se copia. */
80
+ this.value = input('', /* @ts-ignore */
81
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
82
+ /** Muestra el texto ("Copiar" / "Copiado") junto al icono. */
83
+ this.showLabel = input(false, { ...(ngDevMode ? { debugName: "showLabel" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
84
+ /** Texto del botón y nombre accesible. Por defecto, "Copiar" del idioma activo. */
85
+ this.label = input(undefined, /* @ts-ignore */
86
+ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
87
+ /** Texto tras copiar. Por defecto, "Copiado". */
88
+ this.copiedLabel = input(undefined, /* @ts-ignore */
89
+ ...(ngDevMode ? [{ debugName: "copiedLabel" }] : /* istanbul ignore next */ []));
90
+ /** Milisegundos que dura la confirmación. */
91
+ this.timeout = input(2000, /* @ts-ignore */
92
+ ...(ngDevMode ? [{ debugName: "timeout" }] : /* istanbul ignore next */ []));
93
+ this.variant = input('text', /* @ts-ignore */
94
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
95
+ this.severity = input('secondary', /* @ts-ignore */
96
+ ...(ngDevMode ? [{ debugName: "severity" }] : /* istanbul ignore next */ []));
97
+ this.size = input('sm', /* @ts-ignore */
98
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
99
+ this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
100
+ /** Texto copiado. */
101
+ this.copied = output();
102
+ /** El navegador no permitió copiar. */
103
+ this.copyError = output();
104
+ this.state = signal('idle', /* @ts-ignore */
105
+ ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
106
+ this.buttonLabel = computed(() => this.label() ?? this.t().copy, /* @ts-ignore */
107
+ ...(ngDevMode ? [{ debugName: "buttonLabel" }] : /* istanbul ignore next */ []));
108
+ this.visibleLabel = computed(() => this.state() === 'copied' ? (this.copiedLabel() ?? this.t().copied) : this.buttonLabel(), /* @ts-ignore */
109
+ ...(ngDevMode ? [{ debugName: "visibleLabel" }] : /* istanbul ignore next */ []));
110
+ this.announcement = computed(() => {
111
+ const state = this.state();
112
+ if (state === 'copied')
113
+ return this.copiedLabel() ?? this.t().copied;
114
+ if (state === 'error')
115
+ return this.t().copyFailed;
116
+ return '';
117
+ }, /* @ts-ignore */
118
+ ...(ngDevMode ? [{ debugName: "announcement" }] : /* istanbul ignore next */ []));
119
+ this.iconSize = computed(() => (this.size() === 'lg' ? 18 : this.size() === 'sm' ? 14 : 16), /* @ts-ignore */
120
+ ...(ngDevMode ? [{ debugName: "iconSize" }] : /* istanbul ignore next */ []));
121
+ inject(DestroyRef).onDestroy(() => this.clearTimer());
122
+ }
123
+ /** Copia el valor y muestra la confirmación. Resuelve `true` si lo consiguió. */
124
+ async copy() {
125
+ if (this.disabled())
126
+ return false;
127
+ const text = String(this.value() ?? '');
128
+ const ok = await this.clipboard.copy(text);
129
+ this.state.set(ok ? 'copied' : 'error');
130
+ if (ok)
131
+ this.copied.emit(text);
132
+ else
133
+ this.copyError.emit(text);
134
+ this.clearTimer();
135
+ this.timer = setTimeout(() => this.state.set('idle'), Math.max(0, this.timeout()));
136
+ return ok;
137
+ }
138
+ clearTimer() {
139
+ if (this.timer !== null)
140
+ clearTimeout(this.timer);
141
+ this.timer = null;
142
+ }
143
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbCopyButton, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
144
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbCopyButton, isStandalone: true, selector: "cb-copy-button", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, showLabel: { classPropertyName: "showLabel", publicName: "showLabel", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, copiedLabel: { classPropertyName: "copiedLabel", publicName: "copiedLabel", isSignal: true, isRequired: false, transformFunction: null }, timeout: { classPropertyName: "timeout", publicName: "timeout", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, severity: { classPropertyName: "severity", publicName: "severity", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { copied: "copied", copyError: "copyError" }, host: { classAttribute: "cb-copy-button" }, ngImport: i0, template: `
145
+ <cb-button
146
+ [variant]="variant()"
147
+ [severity]="state() === 'copied' ? 'success' : severity()"
148
+ [size]="size()"
149
+ [disabled]="disabled()"
150
+ [ariaLabel]="showLabel() ? undefined : buttonLabel()"
151
+ (click)="copy()"
152
+ >
153
+ <cb-icon [name]="state() === 'copied' ? 'check' : 'copy'" [size]="iconSize()" aria-hidden="true" />
154
+ @if (showLabel()) {
155
+ <span>{{ visibleLabel() }}</span>
156
+ }
157
+ </cb-button>
158
+ <span class="cb-copy-button__status" role="status">{{ announcement() }}</span>
159
+ `, isInline: true, styles: [":host{display:inline-flex;vertical-align:middle}.cb-copy-button__status{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: CbButton, selector: "cb-button", inputs: ["severity", "variant", "size", "disabled", "loading", "fullWidth", "type", "ariaLabel", "ariaHaspopup", "ariaExpanded", "ariaControls"] }, { kind: "component", type: CbIcon, selector: "cb-icon", inputs: ["name", "size", "spin", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
160
+ }
161
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbCopyButton, decorators: [{
162
+ type: Component,
163
+ args: [{ selector: 'cb-copy-button', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [CbButton, CbIcon], host: { class: 'cb-copy-button' }, template: `
164
+ <cb-button
165
+ [variant]="variant()"
166
+ [severity]="state() === 'copied' ? 'success' : severity()"
167
+ [size]="size()"
168
+ [disabled]="disabled()"
169
+ [ariaLabel]="showLabel() ? undefined : buttonLabel()"
170
+ (click)="copy()"
171
+ >
172
+ <cb-icon [name]="state() === 'copied' ? 'check' : 'copy'" [size]="iconSize()" aria-hidden="true" />
173
+ @if (showLabel()) {
174
+ <span>{{ visibleLabel() }}</span>
175
+ }
176
+ </cb-button>
177
+ <span class="cb-copy-button__status" role="status">{{ announcement() }}</span>
178
+ `, styles: [":host{display:inline-flex;vertical-align:middle}.cb-copy-button__status{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"] }]
179
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], showLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLabel", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], copiedLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "copiedLabel", required: false }] }], timeout: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeout", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], severity: [{ type: i0.Input, args: [{ isSignal: true, alias: "severity", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], copied: [{ type: i0.Output, args: ["copied"] }], copyError: [{ type: i0.Output, args: ["copyError"] }] } });
180
+
181
+ /**
182
+ * Copia un texto al portapapeles al pulsar el elemento (botón, enlace, icono…).
183
+ * Para el aviso visual usa `cb-copy-button`, o reacciona a `copied`.
184
+ *
185
+ * @example
186
+ * ```html
187
+ * <button type="button" [cbCopy]="cuenta" (copied)="toast.add({ summary: 'Copiado' })">Copiar</button>
188
+ * ```
189
+ */
190
+ class CbCopy {
191
+ constructor() {
192
+ this.clipboard = inject(CbClipboard);
193
+ /** Texto que se copia. */
194
+ this.cbCopy = input('', /* @ts-ignore */
195
+ ...(ngDevMode ? [{ debugName: "cbCopy" }] : /* istanbul ignore next */ []));
196
+ /** Texto copiado. */
197
+ this.copied = output();
198
+ /** El navegador no permitió copiar. */
199
+ this.copyError = output();
200
+ }
201
+ /** Copia el texto. Resuelve `true` si lo consiguió. */
202
+ async copy() {
203
+ const text = String(this.cbCopy() ?? '');
204
+ const ok = await this.clipboard.copy(text);
205
+ if (ok)
206
+ this.copied.emit(text);
207
+ else
208
+ this.copyError.emit(text);
209
+ return ok;
210
+ }
211
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbCopy, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
212
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: CbCopy, isStandalone: true, selector: "[cbCopy]", inputs: { cbCopy: { classPropertyName: "cbCopy", publicName: "cbCopy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { copied: "copied", copyError: "copyError" }, host: { listeners: { "click": "copy()" } }, ngImport: i0 }); }
213
+ }
214
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbCopy, decorators: [{
215
+ type: Directive,
216
+ args: [{
217
+ selector: '[cbCopy]',
218
+ host: { '(click)': 'copy()' },
219
+ }]
220
+ }], propDecorators: { cbCopy: [{ type: i0.Input, args: [{ isSignal: true, alias: "cbCopy", required: false }] }], copied: [{ type: i0.Output, args: ["copied"] }], copyError: [{ type: i0.Output, args: ["copyError"] }] } });
221
+
222
+ /*
223
+ * Public API Surface de @caribeng/ui/copy-button
224
+ */
225
+
226
+ /**
227
+ * Generated bundle index. Do not edit.
228
+ */
229
+
230
+ export { CbClipboard, CbCopy, CbCopyButton };
@@ -1,7 +1,7 @@
1
1
  import { NgTemplateOutlet } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
3
  import { inject, TemplateRef, Directive, input, model, contentChild, linkedSignal, computed, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
4
- import { injectCbTranslation } from '@caribeng/ui';
4
+ import { injectCbTranslation, cbFormat } from '@caribeng/ui';
5
5
  import { CbPaginator } from '@caribeng/ui/paginator';
6
6
 
7
7
  /** Plantilla de cada elemento de un `<cb-dataview>`. */
@@ -47,6 +47,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
47
47
  class CbDataView {
48
48
  constructor() {
49
49
  this.t = injectCbTranslation();
50
+ this.fmt = cbFormat;
50
51
  /** Colección a mostrar. */
51
52
  this.value = input([], /* @ts-ignore */
52
53
  ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
@@ -138,6 +139,7 @@ class CbDataView {
138
139
  type="button"
139
140
  class="cb-dataview__select"
140
141
  [attr.aria-pressed]="isSelected(item)"
142
+ [attr.aria-label]="fmt(t().selectItem, asItem(item).title)"
141
143
  (click)="selection.set(asItem(item))"
142
144
  >
143
145
  {{ t().select }}
@@ -204,6 +206,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
204
206
  type="button"
205
207
  class="cb-dataview__select"
206
208
  [attr.aria-pressed]="isSelected(item)"
209
+ [attr.aria-label]="fmt(t().selectItem, asItem(item).title)"
207
210
  (click)="selection.set(asItem(item))"
208
211
  >
209
212
  {{ t().select }}
@@ -1,11 +1,17 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, output, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
2
+ import { inject, ElementRef, input, output, computed, signal, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
3
3
  import { CbIcon } from '@caribeng/ui/icon';
4
4
  import { injectCbTranslation } from '@caribeng/ui';
5
5
 
6
- /** Compact application launcher with configurable orientation. */
6
+ /**
7
+ * Compact application launcher with configurable orientation.
8
+ *
9
+ * Sigue el patrón WAI-ARIA *toolbar*: un solo elemento entra en el orden de
10
+ * tabulación y las flechas (según la orientación), Inicio y Fin recorren el resto.
11
+ */
7
12
  class CbDock {
8
13
  constructor() {
14
+ this.host = inject(ElementRef);
9
15
  /** Textos de interfaz en el idioma activo (`provideCaribeNG({ locale })`). */
10
16
  this.t = injectCbTranslation();
11
17
  this.items = input([], /* @ts-ignore */
@@ -15,19 +21,53 @@ class CbDock {
15
21
  this.ariaLabel = input(undefined, /* @ts-ignore */
16
22
  ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
17
23
  this.itemSelect = output();
24
+ this.vertical = computed(() => this.position() === 'left' || this.position() === 'right', /* @ts-ignore */
25
+ ...(ngDevMode ? [{ debugName: "vertical" }] : /* istanbul ignore next */ []));
26
+ /** Último elemento enfocado (el que recibe Tab al volver). */
27
+ this.active = signal(0, /* @ts-ignore */
28
+ ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
29
+ /** Elemento con `tabindex="0"`: el activo si se puede enfocar, si no el primero habilitado. */
30
+ this.tabStop = computed(() => {
31
+ const items = this.items();
32
+ if (items[this.active()] && !items[this.active()].disabled)
33
+ return this.active();
34
+ return items.findIndex((item) => !item.disabled);
35
+ }, /* @ts-ignore */
36
+ ...(ngDevMode ? [{ debugName: "tabStop" }] : /* istanbul ignore next */ []));
18
37
  }
19
38
  select(item) { if (!item.disabled)
20
39
  this.itemSelect.emit(item); }
40
+ onKeydown(event) {
41
+ const [prev, next] = this.vertical() ? ['ArrowUp', 'ArrowDown'] : ['ArrowLeft', 'ArrowRight'];
42
+ const enabled = this.items().flatMap((item, i) => (item.disabled ? [] : [i]));
43
+ if (!enabled.length)
44
+ return;
45
+ const at = enabled.indexOf(this.tabStop());
46
+ let target;
47
+ if (event.key === next)
48
+ target = enabled[(at + 1) % enabled.length];
49
+ else if (event.key === prev)
50
+ target = enabled[(at - 1 + enabled.length) % enabled.length];
51
+ else if (event.key === 'Home')
52
+ target = enabled[0];
53
+ else if (event.key === 'End')
54
+ target = enabled[enabled.length - 1];
55
+ else
56
+ return;
57
+ event.preventDefault();
58
+ this.active.set(target);
59
+ this.host.nativeElement.querySelectorAll('.cb-dock__item')[target]?.focus();
60
+ }
21
61
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbDock, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
22
62
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbDock, isStandalone: true, selector: "cb-dock", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemSelect: "itemSelect" }, host: { properties: { "class.cb-dock--top": "position() === \"top\"", "class.cb-dock--right": "position() === \"right\"", "class.cb-dock--bottom": "position() === \"bottom\"", "class.cb-dock--left": "position() === \"left\"" }, classAttribute: "cb-dock" }, ngImport: i0, template: `
23
63
  <nav [attr.aria-label]="(ariaLabel() ?? t().appDock)">
24
- <ul class="cb-dock__list" role="toolbar" [attr.aria-orientation]="position() === 'left' || position() === 'right' ? 'vertical' : 'horizontal'">
25
- @for (item of items(); track $index) {
26
- <li>
64
+ <ul class="cb-dock__list" role="toolbar" [attr.aria-orientation]="vertical() ? 'vertical' : 'horizontal'" (keydown)="onKeydown($event)">
65
+ @for (item of items(); track $index; let i = $index) {
66
+ <li role="none">
27
67
  @if (item.url && !item.disabled) {
28
- <a class="cb-dock__item" [href]="item.url" [attr.aria-label]="item.label" [title]="item.label" (click)="select(item)"><cb-icon [name]="item.icon" [size]="22" aria-hidden="true" /></a>
68
+ <a class="cb-dock__item" [href]="item.url" [attr.tabindex]="i === tabStop() ? 0 : -1" [attr.aria-label]="item.label" [title]="item.label" (click)="select(item)" (focus)="active.set(i)"><cb-icon [name]="item.icon" [size]="22" aria-hidden="true" /></a>
29
69
  } @else {
30
- <button class="cb-dock__item" type="button" [disabled]="item.disabled" [attr.aria-label]="item.label" [title]="item.label" (click)="select(item)"><cb-icon [name]="item.icon" [size]="22" aria-hidden="true" /></button>
70
+ <button class="cb-dock__item" type="button" [disabled]="item.disabled" [attr.tabindex]="i === tabStop() ? 0 : -1" [attr.aria-label]="item.label" [title]="item.label" (click)="select(item)" (focus)="active.set(i)"><cb-icon [name]="item.icon" [size]="22" aria-hidden="true" /></button>
31
71
  }
32
72
  </li>
33
73
  }
@@ -45,13 +85,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
45
85
  '[class.cb-dock--left]': 'position() === "left"',
46
86
  }, template: `
47
87
  <nav [attr.aria-label]="(ariaLabel() ?? t().appDock)">
48
- <ul class="cb-dock__list" role="toolbar" [attr.aria-orientation]="position() === 'left' || position() === 'right' ? 'vertical' : 'horizontal'">
49
- @for (item of items(); track $index) {
50
- <li>
88
+ <ul class="cb-dock__list" role="toolbar" [attr.aria-orientation]="vertical() ? 'vertical' : 'horizontal'" (keydown)="onKeydown($event)">
89
+ @for (item of items(); track $index; let i = $index) {
90
+ <li role="none">
51
91
  @if (item.url && !item.disabled) {
52
- <a class="cb-dock__item" [href]="item.url" [attr.aria-label]="item.label" [title]="item.label" (click)="select(item)"><cb-icon [name]="item.icon" [size]="22" aria-hidden="true" /></a>
92
+ <a class="cb-dock__item" [href]="item.url" [attr.tabindex]="i === tabStop() ? 0 : -1" [attr.aria-label]="item.label" [title]="item.label" (click)="select(item)" (focus)="active.set(i)"><cb-icon [name]="item.icon" [size]="22" aria-hidden="true" /></a>
53
93
  } @else {
54
- <button class="cb-dock__item" type="button" [disabled]="item.disabled" [attr.aria-label]="item.label" [title]="item.label" (click)="select(item)"><cb-icon [name]="item.icon" [size]="22" aria-hidden="true" /></button>
94
+ <button class="cb-dock__item" type="button" [disabled]="item.disabled" [attr.tabindex]="i === tabStop() ? 0 : -1" [attr.aria-label]="item.label" [title]="item.label" (click)="select(item)" (focus)="active.set(i)"><cb-icon [name]="item.icon" [size]="22" aria-hidden="true" /></button>
55
95
  }
56
96
  </li>
57
97
  }