@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.
- package/README.md +93 -11
- package/fesm2022/caribeng-ui-accordion.mjs +2 -2
- package/fesm2022/caribeng-ui-avatar.mjs +2 -2
- package/fesm2022/caribeng-ui-badge.mjs +8 -2
- package/fesm2022/caribeng-ui-breadcrumb.mjs +13 -6
- package/fesm2022/caribeng-ui-chart.mjs +8 -5
- package/fesm2022/caribeng-ui-chat.mjs +1303 -0
- package/fesm2022/caribeng-ui-code-block.mjs +332 -0
- package/fesm2022/caribeng-ui-colorpicker.mjs +99 -5
- package/fesm2022/caribeng-ui-command-palette.mjs +476 -0
- package/fesm2022/caribeng-ui-confirmdialog.mjs +78 -13
- package/fesm2022/caribeng-ui-confirmpopup.mjs +91 -16
- package/fesm2022/caribeng-ui-contextmenu.mjs +8 -4
- package/fesm2022/caribeng-ui-copy-button.mjs +230 -0
- package/fesm2022/caribeng-ui-dataview.mjs +4 -1
- package/fesm2022/caribeng-ui-dock.mjs +52 -12
- package/fesm2022/caribeng-ui-drawer.mjs +9 -4
- package/fesm2022/caribeng-ui-editor.mjs +21 -0
- package/fesm2022/caribeng-ui-fieldset.mjs +2 -2
- package/fesm2022/caribeng-ui-file-card.mjs +134 -17
- package/fesm2022/caribeng-ui-folder-card.mjs +122 -12
- package/fesm2022/caribeng-ui-inlinemessage.mjs +2 -2
- package/fesm2022/caribeng-ui-inputmask.mjs +8 -2
- package/fesm2022/caribeng-ui-kanban.mjs +4612 -0
- package/fesm2022/caribeng-ui-kbd.mjs +122 -0
- package/fesm2022/caribeng-ui-megamenu.mjs +13 -9
- package/fesm2022/caribeng-ui-mentions.mjs +495 -0
- package/fesm2022/caribeng-ui-menu.mjs +8 -4
- package/fesm2022/caribeng-ui-menubar.mjs +9 -5
- package/fesm2022/caribeng-ui-message.mjs +6 -3
- package/fesm2022/caribeng-ui-organizationchart.mjs +6 -10
- package/fesm2022/caribeng-ui-panel.mjs +6 -6
- package/fesm2022/caribeng-ui-panelmenu.mjs +9 -6
- package/fesm2022/caribeng-ui-phone-input.mjs +688 -0
- package/fesm2022/caribeng-ui-pricing.mjs +263 -69
- package/fesm2022/caribeng-ui-qrcode.mjs +564 -0
- package/fesm2022/caribeng-ui-ripple.mjs +2 -0
- package/fesm2022/caribeng-ui-scrolltop.mjs +3 -1
- package/fesm2022/caribeng-ui-sidebar.mjs +573 -0
- package/fesm2022/caribeng-ui-stepper.mjs +10 -3
- package/fesm2022/caribeng-ui-table.mjs +10 -4
- package/fesm2022/caribeng-ui-tabmenu.mjs +8 -4
- package/fesm2022/caribeng-ui-tieredmenu.mjs +109 -19
- package/fesm2022/caribeng-ui.mjs +388 -1
- package/package.json +42 -2
- package/schematics/collection.json +5 -0
- package/schematics/migrate-from-primeng/core-icons.json +1 -0
- package/schematics/migrate-from-primeng/icons.js +269 -0
- package/schematics/migrate-from-primeng/index.js +290 -0
- package/schematics/migrate-from-primeng/mappings/button.js +93 -0
- package/schematics/migrate-from-primeng/mappings/card.js +33 -0
- package/schematics/migrate-from-primeng/mappings/checkbox.js +44 -0
- package/schematics/migrate-from-primeng/mappings/confirm.js +221 -0
- package/schematics/migrate-from-primeng/mappings/datepicker.js +121 -0
- package/schematics/migrate-from-primeng/mappings/dialog.js +84 -0
- package/schematics/migrate-from-primeng/mappings/index.js +88 -0
- package/schematics/migrate-from-primeng/mappings/inputnumber.js +62 -0
- package/schematics/migrate-from-primeng/mappings/inputtext.js +60 -0
- package/schematics/migrate-from-primeng/mappings/menu.js +468 -0
- package/schematics/migrate-from-primeng/mappings/multiselect.js +49 -0
- package/schematics/migrate-from-primeng/mappings/radiobutton.js +22 -0
- package/schematics/migrate-from-primeng/mappings/rules.js +206 -0
- package/schematics/migrate-from-primeng/mappings/select.js +97 -0
- package/schematics/migrate-from-primeng/mappings/service-calls.js +100 -0
- package/schematics/migrate-from-primeng/mappings/table.js +613 -0
- package/schematics/migrate-from-primeng/mappings/tabs.js +225 -0
- package/schematics/migrate-from-primeng/mappings/tag.js +29 -0
- package/schematics/migrate-from-primeng/mappings/textarea.js +44 -0
- package/schematics/migrate-from-primeng/mappings/toast.js +173 -0
- package/schematics/migrate-from-primeng/mappings/toggleswitch.js +37 -0
- package/schematics/migrate-from-primeng/mappings/tooltip.js +44 -0
- package/schematics/migrate-from-primeng/mappings/types.js +10 -0
- package/schematics/migrate-from-primeng/report.js +79 -0
- package/schematics/migrate-from-primeng/schema.js +2 -0
- package/schematics/migrate-from-primeng/schema.json +34 -0
- package/schematics/migrate-from-primeng/template.js +387 -0
- package/schematics/migrate-from-primeng/todo.js +5 -0
- package/schematics/migrate-from-primeng/typescript.js +442 -0
- package/schematics/migrate-from-primeng/workspace.js +117 -0
- package/schematics/ng-add/index.js +16 -1
- package/types/caribeng-ui-badge.d.ts +3 -1
- package/types/caribeng-ui-breadcrumb.d.ts +8 -0
- package/types/caribeng-ui-chart.d.ts +3 -1
- package/types/caribeng-ui-chat.d.ts +299 -0
- package/types/caribeng-ui-code-block.d.ts +65 -0
- package/types/caribeng-ui-colorpicker.d.ts +5 -1
- package/types/caribeng-ui-command-palette.d.ts +133 -0
- package/types/caribeng-ui-confirmdialog.d.ts +34 -2
- package/types/caribeng-ui-confirmpopup.d.ts +39 -2
- package/types/caribeng-ui-contextmenu.d.ts +7 -0
- package/types/caribeng-ui-copy-button.d.ts +90 -0
- package/types/caribeng-ui-dataview.d.ts +2 -0
- package/types/caribeng-ui-dock.d.ts +13 -1
- package/types/caribeng-ui-drawer.d.ts +3 -1
- package/types/caribeng-ui-file-card.d.ts +27 -2
- package/types/caribeng-ui-folder-card.d.ts +27 -5
- package/types/caribeng-ui-inputmask.d.ts +3 -1
- package/types/caribeng-ui-kanban.d.ts +783 -0
- package/types/caribeng-ui-kbd.d.ts +36 -0
- package/types/caribeng-ui-megamenu.d.ts +11 -0
- package/types/caribeng-ui-mentions.d.ts +127 -0
- package/types/caribeng-ui-menu.d.ts +7 -0
- package/types/caribeng-ui-menubar.d.ts +7 -0
- package/types/caribeng-ui-message.d.ts +2 -0
- package/types/caribeng-ui-panelmenu.d.ts +7 -0
- package/types/caribeng-ui-phone-input.d.ts +168 -0
- package/types/caribeng-ui-pricing.d.ts +41 -4
- package/types/caribeng-ui-qrcode.d.ts +88 -0
- package/types/caribeng-ui-sidebar.d.ts +167 -0
- package/types/caribeng-ui-stepper.d.ts +2 -0
- package/types/caribeng-ui-table.d.ts +1 -1
- package/types/caribeng-ui-tabmenu.d.ts +7 -0
- package/types/caribeng-ui-tieredmenu.d.ts +17 -1
- package/types/caribeng-ui.d.ts +276 -2
- package/styles/caribeng.css +0 -380
- package/styles/scss/_mixins.scss +0 -46
- package/styles/scss/_overlay.scss +0 -15
- package/styles/scss/_tokens.scss +0 -267
- package/styles/scss/_utilities.scss +0 -73
- package/styles/scss/_variables.scss +0 -33
- 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.
|
|
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.
|
|
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">{{
|
|
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
|
-
{{
|
|
185
|
+
{{ shown().rejectLabel }}
|
|
114
186
|
</cb-button>
|
|
115
|
-
<cb-button size="sm" [severity]="
|
|
116
|
-
{{
|
|
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">{{
|
|
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
|
-
{{
|
|
213
|
+
{{ shown().rejectLabel }}
|
|
142
214
|
</cb-button>
|
|
143
|
-
<cb-button size="sm" [severity]="
|
|
144
|
-
{{
|
|
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
|
|
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
|
|
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
|
-
/**
|
|
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]="
|
|
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]="
|
|
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
|
}
|