codexly-ui 0.10.113 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/codexly-ui.mjs +405 -1
- package/fesm2022/codexly-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/types/codexly-ui.d.ts +137 -3
package/fesm2022/codexly-ui.mjs
CHANGED
|
@@ -627,6 +627,28 @@ class ClxThemeService {
|
|
|
627
627
|
bordered: t?.bordered ?? true,
|
|
628
628
|
};
|
|
629
629
|
}, ...(ngDevMode ? [{ debugName: "paginationStyle" }] : /* istanbul ignore next */ []));
|
|
630
|
+
/** Resolved style for ClxNotificationComponent — severity colors fall back to built-in
|
|
631
|
+
* green/amber/sky/red, unreadColor to primaryColor, borderColor to listBorderColor then
|
|
632
|
+
* primaryColor, radius to the app-wide borderRadius. Per-instance inputs on clx-notification
|
|
633
|
+
* still take precedence over this theme-wide default. */
|
|
634
|
+
notificationStyle = computed(() => {
|
|
635
|
+
const n = this._config().notification;
|
|
636
|
+
return {
|
|
637
|
+
severityColors: {
|
|
638
|
+
success: n?.severityColors?.success ?? this._config().successColor,
|
|
639
|
+
warning: n?.severityColors?.warning ?? this._config().warningColor,
|
|
640
|
+
info: n?.severityColors?.info ?? 'sky',
|
|
641
|
+
error: n?.severityColors?.error ?? this._config().dangerColor,
|
|
642
|
+
},
|
|
643
|
+
unreadColor: n?.unreadColor ?? this._config().primaryColor,
|
|
644
|
+
size: n?.size ?? 'md',
|
|
645
|
+
borderColor: n?.borderColor ?? this._config().listBorderColor ?? this._config().primaryColor,
|
|
646
|
+
radius: n?.radius ?? this._config().borderRadius,
|
|
647
|
+
titleWeight: n?.titleWeight ?? 600,
|
|
648
|
+
detailColor: n?.detailColor,
|
|
649
|
+
groupLabelWeight: n?.groupLabelWeight ?? 700,
|
|
650
|
+
};
|
|
651
|
+
}, ...(ngDevMode ? [{ debugName: "notificationStyle" }] : /* istanbul ignore next */ []));
|
|
630
652
|
/** Resolved text color for solid primary/secondary buttons — undefined means "let
|
|
631
653
|
* resolveColor() auto-pick white/dark-800 based on shade", same as today. */
|
|
632
654
|
buttonTextColor(role) {
|
|
@@ -9581,6 +9603,13 @@ class ClxTabsComponent {
|
|
|
9581
9603
|
_clxColor = computed(() => this.clxColor() ?? this._themeSvc.config().primaryColor, ...(ngDevMode ? [{ debugName: "_clxColor" }] : /* istanbul ignore next */ []));
|
|
9582
9604
|
_panels = contentChildren(ClxTabDirective, ...(ngDevMode ? [{ debugName: "_panels" }] : /* istanbul ignore next */ []));
|
|
9583
9605
|
_underlineClass = computed(() => resolveColor(this._clxColor()).bg, ...(ngDevMode ? [{ debugName: "_underlineClass" }] : /* istanbul ignore next */ []));
|
|
9606
|
+
_badgeClass(tabId) {
|
|
9607
|
+
const active = this.activeTab() === tabId;
|
|
9608
|
+
const t = resolveColor(this._clxColor());
|
|
9609
|
+
return active
|
|
9610
|
+
? `ml-0.5 px-1.5 py-0.5 rounded-full text-[10px] font-bold leading-none tabular-nums ${t.bgSubtle} ${t.textSubtle}`
|
|
9611
|
+
: 'ml-0.5 px-1.5 py-0.5 rounded-full text-[10px] font-bold leading-none tabular-nums bg-clx-surface-2 text-clx-text-subtle';
|
|
9612
|
+
}
|
|
9584
9613
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9585
9614
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxTabsComponent, isStandalone: true, selector: "clx-tabs", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: true, transformFunction: null }, clxColor: { classPropertyName: "clxColor", publicName: "clxColor", isSignal: true, isRequired: false, transformFunction: null }, activeTab: { classPropertyName: "activeTab", publicName: "activeTab", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeTab: "activeTabChange" }, queries: [{ propertyName: "_panels", predicate: ClxTabDirective, isSignal: true }], ngImport: i0, template: `
|
|
9586
9615
|
<!-- Tab bar -->
|
|
@@ -9603,6 +9632,9 @@ class ClxTabsComponent {
|
|
|
9603
9632
|
<span clx-icon [name]="tab.icon" size="xs" class="shrink-0"></span>
|
|
9604
9633
|
}
|
|
9605
9634
|
{{ tab.label }}
|
|
9635
|
+
@if (tab.badge !== undefined) {
|
|
9636
|
+
<span [class]="_badgeClass(tab.id)">{{ tab.badge }}</span>
|
|
9637
|
+
}
|
|
9606
9638
|
</button>
|
|
9607
9639
|
@if (activeTab() === tab.id) {
|
|
9608
9640
|
<span class="absolute bottom-0 left-0 right-0 h-0.5 rounded-full {{ _underlineClass() }}"></span>
|
|
@@ -9644,6 +9676,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
9644
9676
|
<span clx-icon [name]="tab.icon" size="xs" class="shrink-0"></span>
|
|
9645
9677
|
}
|
|
9646
9678
|
{{ tab.label }}
|
|
9679
|
+
@if (tab.badge !== undefined) {
|
|
9680
|
+
<span [class]="_badgeClass(tab.id)">{{ tab.badge }}</span>
|
|
9681
|
+
}
|
|
9647
9682
|
</button>
|
|
9648
9683
|
@if (activeTab() === tab.id) {
|
|
9649
9684
|
<span class="absolute bottom-0 left-0 right-0 h-0.5 rounded-full {{ _underlineClass() }}"></span>
|
|
@@ -11236,6 +11271,375 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
11236
11271
|
}]
|
|
11237
11272
|
}], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], showClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClose", required: false }] }], confirmButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmButton", required: false }] }], cancelButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelButton", required: false }] }], showCancelButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCancelButton", required: false }] }], confirmClick: [{ type: i0.Output, args: ["confirmClick"] }], cancelClick: [{ type: i0.Output, args: ["cancelClick"] }] } });
|
|
11238
11273
|
|
|
11274
|
+
// ── Size map — same density-token shape as LIST_SIZE_MAP ──────────────────────
|
|
11275
|
+
const NOTIFICATION_SIZE_MAP = {
|
|
11276
|
+
sm: { row: 'py-2', iconWrap: 'w-7 h-7', iconSize: 'xs', title: 'text-xs', detail: 'text-[11px]', time: 'text-[10px]' },
|
|
11277
|
+
md: { row: 'py-2.5', iconWrap: 'w-8.5 h-8.5', iconSize: 'sm', title: 'text-[13px]', detail: 'text-xs', time: 'text-[11px]' },
|
|
11278
|
+
lg: { row: 'py-3', iconWrap: 'w-10 h-10', iconSize: 'md', title: 'text-sm', detail: 'text-[13px]', time: 'text-xs' },
|
|
11279
|
+
};
|
|
11280
|
+
// ── Default icon per severity — used when an item doesn't specify its own `icon` ──
|
|
11281
|
+
const NOTIFICATION_SEVERITY_ICON = {
|
|
11282
|
+
success: 'check_circle',
|
|
11283
|
+
warning: 'notifications_active',
|
|
11284
|
+
info: 'info',
|
|
11285
|
+
error: 'error',
|
|
11286
|
+
};
|
|
11287
|
+
|
|
11288
|
+
/** Self-contained bell trigger + floating panel — same overlay pattern as clx-profile (CDK
|
|
11289
|
+
* Overlay anchored to the trigger, no page-level backdrop), not a full-height clx-drawer. It's
|
|
11290
|
+
* "dumb" beyond that: renders whatever `items()` it's given and emits intent (itemClick/
|
|
11291
|
+
* markRead/delete/...) outward. Callers (e.g. a NotificationService backed by SSE) own fetching
|
|
11292
|
+
* and persistence — this component never calls an API itself. */
|
|
11293
|
+
class ClxNotificationComponent {
|
|
11294
|
+
_themeSvc = inject(ClxThemeService);
|
|
11295
|
+
items = input.required(...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
11296
|
+
color = input(undefined, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
|
|
11297
|
+
/** Optional third tab that filters items by ClxNotificationItem.category — e.g.
|
|
11298
|
+
* `{ id: 'orders', label: 'Órdenes' }`. Omit for just Todas/Sin leer. */
|
|
11299
|
+
categoryTab = input(undefined, ...(ngDevMode ? [{ debugName: "categoryTab" }] : /* istanbul ignore next */ []));
|
|
11300
|
+
itemClick = output();
|
|
11301
|
+
markRead = output();
|
|
11302
|
+
markAllRead = output();
|
|
11303
|
+
delete = output();
|
|
11304
|
+
deleteAllRead = output();
|
|
11305
|
+
_style = this._themeSvc.notificationStyle;
|
|
11306
|
+
_color = computed(() => this.color() ?? this._themeSvc.config().primaryColor, ...(ngDevMode ? [{ debugName: "_color" }] : /* istanbul ignore next */ []));
|
|
11307
|
+
// ── CDK Overlay ─────────────────────────────────────────────────────────────
|
|
11308
|
+
_sso = inject(ScrollStrategyOptions);
|
|
11309
|
+
_scrollStrategy = this._sso.reposition();
|
|
11310
|
+
_positions = [
|
|
11311
|
+
{ originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 8 },
|
|
11312
|
+
{ originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -8 },
|
|
11313
|
+
];
|
|
11314
|
+
_isOpen = signal(false, ...(ngDevMode ? [{ debugName: "_isOpen" }] : /* istanbul ignore next */ []));
|
|
11315
|
+
_filter = signal('all', ...(ngDevMode ? [{ debugName: "_filter" }] : /* istanbul ignore next */ []));
|
|
11316
|
+
_ringClass = computed(() => {
|
|
11317
|
+
const t = resolveColor(this._color());
|
|
11318
|
+
return `focus-visible:${t.ringBase}`;
|
|
11319
|
+
}, ...(ngDevMode ? [{ debugName: "_ringClass" }] : /* istanbul ignore next */ []));
|
|
11320
|
+
_panelClass = computed(() => `w-96 bg-clx-surface shadow-xl border border-clx-border-soft overflow-hidden mt-2 flex flex-col ${resolveContainerRadius(this._style().radius)}`, ...(ngDevMode ? [{ debugName: "_panelClass" }] : /* istanbul ignore next */ []));
|
|
11321
|
+
_unreadCount = computed(() => this.items().filter(i => !i.read).length, ...(ngDevMode ? [{ debugName: "_unreadCount" }] : /* istanbul ignore next */ []));
|
|
11322
|
+
_readCount = computed(() => this.items().filter(i => i.read).length, ...(ngDevMode ? [{ debugName: "_readCount" }] : /* istanbul ignore next */ []));
|
|
11323
|
+
_tabs = computed(() => {
|
|
11324
|
+
const tabs = [
|
|
11325
|
+
{ id: 'all', label: 'Todas', badge: this.items().length || undefined },
|
|
11326
|
+
{ id: 'unread', label: 'Sin leer', badge: this._unreadCount() || undefined },
|
|
11327
|
+
];
|
|
11328
|
+
const category = this.categoryTab();
|
|
11329
|
+
if (category)
|
|
11330
|
+
tabs.push({ id: category.id, label: category.label });
|
|
11331
|
+
return tabs;
|
|
11332
|
+
}, ...(ngDevMode ? [{ debugName: "_tabs" }] : /* istanbul ignore next */ []));
|
|
11333
|
+
_visibleItems = computed(() => {
|
|
11334
|
+
const f = this._filter();
|
|
11335
|
+
if (f === 'unread')
|
|
11336
|
+
return this.items().filter(i => !i.read);
|
|
11337
|
+
const category = this.categoryTab();
|
|
11338
|
+
if (category && f === category.id)
|
|
11339
|
+
return this.items().filter(i => i.category === category.id);
|
|
11340
|
+
return this.items();
|
|
11341
|
+
}, ...(ngDevMode ? [{ debugName: "_visibleItems" }] : /* istanbul ignore next */ []));
|
|
11342
|
+
_groups = computed(() => {
|
|
11343
|
+
const byLabel = new Map();
|
|
11344
|
+
for (const item of this._visibleItems()) {
|
|
11345
|
+
const label = this._dayLabel(item.createdAt);
|
|
11346
|
+
const bucket = byLabel.get(label);
|
|
11347
|
+
if (bucket)
|
|
11348
|
+
bucket.push(item);
|
|
11349
|
+
else
|
|
11350
|
+
byLabel.set(label, [item]);
|
|
11351
|
+
}
|
|
11352
|
+
return Array.from(byLabel, ([label, items]) => ({ label, items }));
|
|
11353
|
+
}, ...(ngDevMode ? [{ debugName: "_groups" }] : /* istanbul ignore next */ []));
|
|
11354
|
+
_groupLabelClass = computed(() => {
|
|
11355
|
+
const w = this._style().groupLabelWeight;
|
|
11356
|
+
return `px-2 pt-3 pb-1.5 text-[11px] uppercase tracking-wide text-clx-text-subtle font-[${w}]`;
|
|
11357
|
+
}, ...(ngDevMode ? [{ debugName: "_groupLabelClass" }] : /* istanbul ignore next */ []));
|
|
11358
|
+
_linkClass = computed(() => resolveColor(this._color()).textSubtle, ...(ngDevMode ? [{ debugName: "_linkClass" }] : /* istanbul ignore next */ []));
|
|
11359
|
+
_unreadRailClass = computed(() => resolveColor(this._style().unreadColor).bg, ...(ngDevMode ? [{ debugName: "_unreadRailClass" }] : /* istanbul ignore next */ []));
|
|
11360
|
+
_unreadDotClass = computed(() => `w-1.5 h-1.5 rounded-full shrink-0 ${resolveColor(this._style().unreadColor).bg}`, ...(ngDevMode ? [{ debugName: "_unreadDotClass" }] : /* istanbul ignore next */ []));
|
|
11361
|
+
_detailClass = computed(() => this._style().detailColor ? resolveColor(this._style().detailColor).textSubtle : 'text-clx-text-muted', ...(ngDevMode ? [{ debugName: "_detailClass" }] : /* istanbul ignore next */ []));
|
|
11362
|
+
_titleClass(item) {
|
|
11363
|
+
const weight = item.read ? 500 : this._style().titleWeight;
|
|
11364
|
+
return `text-clx-text-label font-[${weight}]`;
|
|
11365
|
+
}
|
|
11366
|
+
_iconWrapClass(item) {
|
|
11367
|
+
const c = resolveColor(this._severityColor(item.severity));
|
|
11368
|
+
return `flex items-center justify-center rounded-lg shrink-0 w-8.5 h-8.5 mt-0.5 ${c.bgSubtle} ${c.textSubtle}`;
|
|
11369
|
+
}
|
|
11370
|
+
_defaultIcon(severity) {
|
|
11371
|
+
return NOTIFICATION_SEVERITY_ICON[severity];
|
|
11372
|
+
}
|
|
11373
|
+
_open(item) {
|
|
11374
|
+
this.itemClick.emit(item);
|
|
11375
|
+
if (!item.read)
|
|
11376
|
+
this.markRead.emit(item.id);
|
|
11377
|
+
}
|
|
11378
|
+
_toggle() { this._isOpen.update(v => !v); }
|
|
11379
|
+
_close() { this._isOpen.set(false); }
|
|
11380
|
+
_onKeydown(event) {
|
|
11381
|
+
if (event.key === 'Escape')
|
|
11382
|
+
this._close();
|
|
11383
|
+
}
|
|
11384
|
+
_severityColor(severity) {
|
|
11385
|
+
return this._style().severityColors[severity];
|
|
11386
|
+
}
|
|
11387
|
+
_dayLabel(iso) {
|
|
11388
|
+
const date = new Date(iso);
|
|
11389
|
+
const now = new Date();
|
|
11390
|
+
const startOf = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
|
|
11391
|
+
const diffDays = Math.round((startOf(now) - startOf(date)) / 86_400_000);
|
|
11392
|
+
if (diffDays === 0)
|
|
11393
|
+
return 'Hoy';
|
|
11394
|
+
if (diffDays === 1)
|
|
11395
|
+
return 'Ayer';
|
|
11396
|
+
if (diffDays > 1 && diffDays < 7)
|
|
11397
|
+
return `Hace ${diffDays} días`;
|
|
11398
|
+
return date.toLocaleDateString('es-CO', { day: 'numeric', month: 'short' });
|
|
11399
|
+
}
|
|
11400
|
+
_timeOf(iso) {
|
|
11401
|
+
return new Date(iso).toLocaleTimeString('es-CO', { hour: '2-digit', minute: '2-digit' });
|
|
11402
|
+
}
|
|
11403
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxNotificationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11404
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxNotificationComponent, isStandalone: true, selector: "clx-notification", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, categoryTab: { classPropertyName: "categoryTab", publicName: "categoryTab", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemClick: "itemClick", markRead: "markRead", markAllRead: "markAllRead", delete: "delete", deleteAllRead: "deleteAllRead" }, host: { classAttribute: "relative inline-flex items-center" }, ngImport: i0, template: `
|
|
11405
|
+
<!-- Trigger -->
|
|
11406
|
+
<button
|
|
11407
|
+
#origin="cdkOverlayOrigin"
|
|
11408
|
+
cdkOverlayOrigin
|
|
11409
|
+
type="button"
|
|
11410
|
+
aria-label="Notificaciones"
|
|
11411
|
+
class="relative flex items-center justify-center w-9 h-9 rounded-full text-clx-text-muted hover:bg-clx-surface-2 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 cursor-pointer"
|
|
11412
|
+
[class]="_ringClass()"
|
|
11413
|
+
(click)="_toggle()">
|
|
11414
|
+
<span clx-icon name="notifications" size="sm"></span>
|
|
11415
|
+
@if (_unreadCount() > 0) {
|
|
11416
|
+
<span clx-badge [color]="_style().unreadColor" variant="solid" size="xs" shape="circle"
|
|
11417
|
+
class="absolute -top-0.5 -right-0.5">{{ _unreadCount() > 9 ? '9+' : _unreadCount() }}</span>
|
|
11418
|
+
}
|
|
11419
|
+
</button>
|
|
11420
|
+
|
|
11421
|
+
<!-- Overlay panel -->
|
|
11422
|
+
<ng-template
|
|
11423
|
+
cdkConnectedOverlay
|
|
11424
|
+
[cdkConnectedOverlayOrigin]="origin"
|
|
11425
|
+
[cdkConnectedOverlayOpen]="_isOpen()"
|
|
11426
|
+
[cdkConnectedOverlayPositions]="_positions"
|
|
11427
|
+
[cdkConnectedOverlayScrollStrategy]="_scrollStrategy"
|
|
11428
|
+
[cdkConnectedOverlayPush]="true"
|
|
11429
|
+
(overlayOutsideClick)="_close()"
|
|
11430
|
+
(overlayKeydown)="_onKeydown($event)">
|
|
11431
|
+
|
|
11432
|
+
<div [class]="_panelClass()">
|
|
11433
|
+
<!-- Header -->
|
|
11434
|
+
<div class="flex items-center justify-between gap-3 px-4 pt-4 pb-1">
|
|
11435
|
+
<p class="text-base font-semibold text-clx-text-label">Notificaciones</p>
|
|
11436
|
+
<div class="flex items-center gap-3">
|
|
11437
|
+
@if (_unreadCount() > 0) {
|
|
11438
|
+
<button type="button" class="text-xs font-semibold shrink-0" [class]="_linkClass()"
|
|
11439
|
+
(click)="markAllRead.emit()">
|
|
11440
|
+
Marcar todo leído
|
|
11441
|
+
</button>
|
|
11442
|
+
}
|
|
11443
|
+
<button type="button" class="w-6 h-6 flex items-center justify-center rounded-md text-clx-text-muted hover:bg-clx-surface-2"
|
|
11444
|
+
aria-label="Cerrar" (click)="_close()">
|
|
11445
|
+
<span clx-icon name="close" size="xs"></span>
|
|
11446
|
+
</button>
|
|
11447
|
+
</div>
|
|
11448
|
+
</div>
|
|
11449
|
+
|
|
11450
|
+
<!-- Tabs -->
|
|
11451
|
+
<div class="px-4">
|
|
11452
|
+
<clx-tabs [tabs]="_tabs()" [(activeTab)]="_filter" [clxColor]="_color()" />
|
|
11453
|
+
</div>
|
|
11454
|
+
|
|
11455
|
+
<!-- List -->
|
|
11456
|
+
<div class="max-h-105 overflow-y-auto px-2 pb-2">
|
|
11457
|
+
@if (_groups().length) {
|
|
11458
|
+
@for (group of _groups(); track group.label) {
|
|
11459
|
+
<p [class]="_groupLabelClass()">{{ group.label }}</p>
|
|
11460
|
+
@for (item of group.items; track item.id) {
|
|
11461
|
+
<div
|
|
11462
|
+
class="group relative flex items-start gap-3 pl-3 pr-2 rounded-lg cursor-pointer hover:bg-clx-surface-2 py-2.5"
|
|
11463
|
+
role="button"
|
|
11464
|
+
tabindex="0"
|
|
11465
|
+
(click)="_open(item)"
|
|
11466
|
+
(keydown.enter)="_open(item)">
|
|
11467
|
+
|
|
11468
|
+
<span class="absolute left-0.5 top-1 bottom-1 w-0.75 rounded-full {{ item.read ? '' : _unreadRailClass() }}"></span>
|
|
11469
|
+
|
|
11470
|
+
<span [class]="_iconWrapClass(item)">
|
|
11471
|
+
<span clx-icon [name]="item.icon || _defaultIcon(item.severity)" size="sm"></span>
|
|
11472
|
+
</span>
|
|
11473
|
+
|
|
11474
|
+
<div class="flex-1 min-w-0">
|
|
11475
|
+
<p class="flex items-baseline gap-1.5 text-sm" [class]="_titleClass(item)">
|
|
11476
|
+
@if (!item.read) {
|
|
11477
|
+
<span [class]="_unreadDotClass()"></span>
|
|
11478
|
+
}
|
|
11479
|
+
<span class="truncate">{{ item.title }}</span>
|
|
11480
|
+
</p>
|
|
11481
|
+
@if (item.detail) {
|
|
11482
|
+
<p class="mt-0.5 text-sm line-clamp-2" [class]="_detailClass()">{{ item.detail }}</p>
|
|
11483
|
+
}
|
|
11484
|
+
<p class="mt-1 text-xs text-clx-text-subtle tabular-nums">{{ _timeOf(item.createdAt) }}</p>
|
|
11485
|
+
</div>
|
|
11486
|
+
|
|
11487
|
+
<button type="button"
|
|
11488
|
+
class="opacity-0 group-hover:opacity-100 focus-visible:opacity-100 transition-opacity shrink-0 rounded-md p-1 text-clx-text-subtle hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-950"
|
|
11489
|
+
aria-label="Eliminar notificación"
|
|
11490
|
+
(click)="$event.stopPropagation(); delete.emit(item.id)">
|
|
11491
|
+
<span clx-icon name="close" size="xs"></span>
|
|
11492
|
+
</button>
|
|
11493
|
+
</div>
|
|
11494
|
+
}
|
|
11495
|
+
}
|
|
11496
|
+
} @else {
|
|
11497
|
+
<div class="flex flex-col items-center justify-center gap-2 py-14 text-center">
|
|
11498
|
+
<span clx-icon name="notifications_off" size="xl" class="text-clx-text-subtle"></span>
|
|
11499
|
+
<p class="text-sm font-medium text-clx-text-label">
|
|
11500
|
+
{{ _filter() === 'unread' ? 'No tienes notificaciones sin leer' : 'No hay notificaciones' }}
|
|
11501
|
+
</p>
|
|
11502
|
+
</div>
|
|
11503
|
+
}
|
|
11504
|
+
</div>
|
|
11505
|
+
|
|
11506
|
+
@if (_readCount() > 0) {
|
|
11507
|
+
<div class="flex justify-end px-4 py-2.5 border-t border-clx-border-soft">
|
|
11508
|
+
<button type="button" class="text-xs font-semibold text-clx-text-subtle hover:text-red-500"
|
|
11509
|
+
(click)="deleteAllRead.emit()">
|
|
11510
|
+
Eliminar leídas
|
|
11511
|
+
</button>
|
|
11512
|
+
</div>
|
|
11513
|
+
}
|
|
11514
|
+
</div>
|
|
11515
|
+
</ng-template>
|
|
11516
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "component", type: ClxBadgeComponent, selector: "span[clx-badge]", inputs: ["variant", "color", "size", "shape", "positioned"] }, { kind: "component", type: ClxTabsComponent, selector: "clx-tabs", inputs: ["tabs", "clxColor", "activeTab"], outputs: ["activeTabChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
11517
|
+
}
|
|
11518
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxNotificationComponent, decorators: [{
|
|
11519
|
+
type: Component,
|
|
11520
|
+
args: [{
|
|
11521
|
+
selector: 'clx-notification',
|
|
11522
|
+
standalone: true,
|
|
11523
|
+
imports: [OverlayModule, ClxIconComponent, ClxBadgeComponent, ClxTabsComponent],
|
|
11524
|
+
template: `
|
|
11525
|
+
<!-- Trigger -->
|
|
11526
|
+
<button
|
|
11527
|
+
#origin="cdkOverlayOrigin"
|
|
11528
|
+
cdkOverlayOrigin
|
|
11529
|
+
type="button"
|
|
11530
|
+
aria-label="Notificaciones"
|
|
11531
|
+
class="relative flex items-center justify-center w-9 h-9 rounded-full text-clx-text-muted hover:bg-clx-surface-2 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 cursor-pointer"
|
|
11532
|
+
[class]="_ringClass()"
|
|
11533
|
+
(click)="_toggle()">
|
|
11534
|
+
<span clx-icon name="notifications" size="sm"></span>
|
|
11535
|
+
@if (_unreadCount() > 0) {
|
|
11536
|
+
<span clx-badge [color]="_style().unreadColor" variant="solid" size="xs" shape="circle"
|
|
11537
|
+
class="absolute -top-0.5 -right-0.5">{{ _unreadCount() > 9 ? '9+' : _unreadCount() }}</span>
|
|
11538
|
+
}
|
|
11539
|
+
</button>
|
|
11540
|
+
|
|
11541
|
+
<!-- Overlay panel -->
|
|
11542
|
+
<ng-template
|
|
11543
|
+
cdkConnectedOverlay
|
|
11544
|
+
[cdkConnectedOverlayOrigin]="origin"
|
|
11545
|
+
[cdkConnectedOverlayOpen]="_isOpen()"
|
|
11546
|
+
[cdkConnectedOverlayPositions]="_positions"
|
|
11547
|
+
[cdkConnectedOverlayScrollStrategy]="_scrollStrategy"
|
|
11548
|
+
[cdkConnectedOverlayPush]="true"
|
|
11549
|
+
(overlayOutsideClick)="_close()"
|
|
11550
|
+
(overlayKeydown)="_onKeydown($event)">
|
|
11551
|
+
|
|
11552
|
+
<div [class]="_panelClass()">
|
|
11553
|
+
<!-- Header -->
|
|
11554
|
+
<div class="flex items-center justify-between gap-3 px-4 pt-4 pb-1">
|
|
11555
|
+
<p class="text-base font-semibold text-clx-text-label">Notificaciones</p>
|
|
11556
|
+
<div class="flex items-center gap-3">
|
|
11557
|
+
@if (_unreadCount() > 0) {
|
|
11558
|
+
<button type="button" class="text-xs font-semibold shrink-0" [class]="_linkClass()"
|
|
11559
|
+
(click)="markAllRead.emit()">
|
|
11560
|
+
Marcar todo leído
|
|
11561
|
+
</button>
|
|
11562
|
+
}
|
|
11563
|
+
<button type="button" class="w-6 h-6 flex items-center justify-center rounded-md text-clx-text-muted hover:bg-clx-surface-2"
|
|
11564
|
+
aria-label="Cerrar" (click)="_close()">
|
|
11565
|
+
<span clx-icon name="close" size="xs"></span>
|
|
11566
|
+
</button>
|
|
11567
|
+
</div>
|
|
11568
|
+
</div>
|
|
11569
|
+
|
|
11570
|
+
<!-- Tabs -->
|
|
11571
|
+
<div class="px-4">
|
|
11572
|
+
<clx-tabs [tabs]="_tabs()" [(activeTab)]="_filter" [clxColor]="_color()" />
|
|
11573
|
+
</div>
|
|
11574
|
+
|
|
11575
|
+
<!-- List -->
|
|
11576
|
+
<div class="max-h-105 overflow-y-auto px-2 pb-2">
|
|
11577
|
+
@if (_groups().length) {
|
|
11578
|
+
@for (group of _groups(); track group.label) {
|
|
11579
|
+
<p [class]="_groupLabelClass()">{{ group.label }}</p>
|
|
11580
|
+
@for (item of group.items; track item.id) {
|
|
11581
|
+
<div
|
|
11582
|
+
class="group relative flex items-start gap-3 pl-3 pr-2 rounded-lg cursor-pointer hover:bg-clx-surface-2 py-2.5"
|
|
11583
|
+
role="button"
|
|
11584
|
+
tabindex="0"
|
|
11585
|
+
(click)="_open(item)"
|
|
11586
|
+
(keydown.enter)="_open(item)">
|
|
11587
|
+
|
|
11588
|
+
<span class="absolute left-0.5 top-1 bottom-1 w-0.75 rounded-full {{ item.read ? '' : _unreadRailClass() }}"></span>
|
|
11589
|
+
|
|
11590
|
+
<span [class]="_iconWrapClass(item)">
|
|
11591
|
+
<span clx-icon [name]="item.icon || _defaultIcon(item.severity)" size="sm"></span>
|
|
11592
|
+
</span>
|
|
11593
|
+
|
|
11594
|
+
<div class="flex-1 min-w-0">
|
|
11595
|
+
<p class="flex items-baseline gap-1.5 text-sm" [class]="_titleClass(item)">
|
|
11596
|
+
@if (!item.read) {
|
|
11597
|
+
<span [class]="_unreadDotClass()"></span>
|
|
11598
|
+
}
|
|
11599
|
+
<span class="truncate">{{ item.title }}</span>
|
|
11600
|
+
</p>
|
|
11601
|
+
@if (item.detail) {
|
|
11602
|
+
<p class="mt-0.5 text-sm line-clamp-2" [class]="_detailClass()">{{ item.detail }}</p>
|
|
11603
|
+
}
|
|
11604
|
+
<p class="mt-1 text-xs text-clx-text-subtle tabular-nums">{{ _timeOf(item.createdAt) }}</p>
|
|
11605
|
+
</div>
|
|
11606
|
+
|
|
11607
|
+
<button type="button"
|
|
11608
|
+
class="opacity-0 group-hover:opacity-100 focus-visible:opacity-100 transition-opacity shrink-0 rounded-md p-1 text-clx-text-subtle hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-950"
|
|
11609
|
+
aria-label="Eliminar notificación"
|
|
11610
|
+
(click)="$event.stopPropagation(); delete.emit(item.id)">
|
|
11611
|
+
<span clx-icon name="close" size="xs"></span>
|
|
11612
|
+
</button>
|
|
11613
|
+
</div>
|
|
11614
|
+
}
|
|
11615
|
+
}
|
|
11616
|
+
} @else {
|
|
11617
|
+
<div class="flex flex-col items-center justify-center gap-2 py-14 text-center">
|
|
11618
|
+
<span clx-icon name="notifications_off" size="xl" class="text-clx-text-subtle"></span>
|
|
11619
|
+
<p class="text-sm font-medium text-clx-text-label">
|
|
11620
|
+
{{ _filter() === 'unread' ? 'No tienes notificaciones sin leer' : 'No hay notificaciones' }}
|
|
11621
|
+
</p>
|
|
11622
|
+
</div>
|
|
11623
|
+
}
|
|
11624
|
+
</div>
|
|
11625
|
+
|
|
11626
|
+
@if (_readCount() > 0) {
|
|
11627
|
+
<div class="flex justify-end px-4 py-2.5 border-t border-clx-border-soft">
|
|
11628
|
+
<button type="button" class="text-xs font-semibold text-clx-text-subtle hover:text-red-500"
|
|
11629
|
+
(click)="deleteAllRead.emit()">
|
|
11630
|
+
Eliminar leídas
|
|
11631
|
+
</button>
|
|
11632
|
+
</div>
|
|
11633
|
+
}
|
|
11634
|
+
</div>
|
|
11635
|
+
</ng-template>
|
|
11636
|
+
`,
|
|
11637
|
+
encapsulation: ViewEncapsulation.None,
|
|
11638
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
11639
|
+
host: { class: 'relative inline-flex items-center' },
|
|
11640
|
+
}]
|
|
11641
|
+
}], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], categoryTab: [{ type: i0.Input, args: [{ isSignal: true, alias: "categoryTab", required: false }] }], itemClick: [{ type: i0.Output, args: ["itemClick"] }], markRead: [{ type: i0.Output, args: ["markRead"] }], markAllRead: [{ type: i0.Output, args: ["markAllRead"] }], delete: [{ type: i0.Output, args: ["delete"] }], deleteAllRead: [{ type: i0.Output, args: ["deleteAllRead"] }] } });
|
|
11642
|
+
|
|
11239
11643
|
// ─── CDK overlay token (carries the resolved ref into the component) ──────────
|
|
11240
11644
|
const CLX_ALERT_OPTIONS = new InjectionToken('CLX_ALERT_OPTIONS');
|
|
11241
11645
|
const CLX_ALERT_RESOLVE = new InjectionToken('CLX_ALERT_RESOLVE');
|
|
@@ -17065,5 +17469,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
17065
17469
|
* Generated bundle index. Do not edit.
|
|
17066
17470
|
*/
|
|
17067
17471
|
|
|
17068
|
-
export { CLX_ADDON_BORDER, CLX_ADDON_TEXT, CLX_ALERT_OPTIONS, CLX_ALERT_RESOLVE, CLX_BG_ADDON, CLX_BG_DISABLED, CLX_BG_ICON_WRAP, CLX_BG_SECTION, CLX_BG_SURFACE, CLX_BORDER_DEFAULT, CLX_BORDER_DISABLED, CLX_BORDER_MEDIUM, CLX_COLOR_HEX, CLX_COLOR_HEX_100, CLX_COLOR_MAP, CLX_FONT_CATALOG, CLX_MODAL_ANIM_CONFIG, CLX_MODAL_DATA, CLX_MODAL_REF, CLX_OPTION_DISABLED, CLX_PLACEHOLDER, CLX_RADIO_GROUP, CLX_RADIUS_MAP, CLX_TEXT_BODY, CLX_TEXT_DISABLED, CLX_TEXT_HEADING, CLX_TEXT_HINT, CLX_TEXT_IDLE, CLX_TEXT_INPUT, CLX_TEXT_LABEL, CLX_TEXT_OPTION, CLX_TEXT_SUBTITLE, CLX_TEXT_TITLE, CLX_THEME_CONFIG, CLX_THEME_DEFAULTS, CLX_TOAST_DEFAULTS, ClxAlertComponent, ClxAlertService, ClxAnimateDirective, ClxAnimateGroupDirective, ClxAnimateService, ClxAppLayoutComponent, ClxAvatarComponent, ClxBadgeComponent, ClxBrandComponent, ClxButtonComponent, ClxButtonGroupComponent, ClxCardBodyDirective, ClxCardComponent, ClxCardFooterDirective, ClxCardHeaderActionsDirective, ClxCardHeaderDirective, ClxCarouselComponent, ClxCarouselDirective, ClxCartComponent, ClxCartSummaryDrawer, ClxCellDirective, ClxCheckboxComponent, ClxCollapseComponent, ClxColorPickerComponent, ClxColumnDefDirective, ClxDateRangePickerComponent, ClxDatepickerComponent, ClxDrawerComponent, ClxDrawerService, ClxEditorComponent, ClxEditorLinkModalComponent, ClxFabComponent, ClxFilterPanelComponent, ClxHeaderCellDirective, ClxIconComponent, ClxInputComponent, ClxListComponent, ClxListItemComponent, ClxMenuComponent, ClxMenuItemComponent, ClxMenuItemTrailingDirective, ClxModalComponent, ClxModalService, ClxNativeOverlayService, ClxNavGroupComponent, ClxNumberComponent, ClxOtpComponent, ClxPageEmptyComponent, ClxPageHeaderComponent, ClxPageHeaderTitleDirective, ClxPageNotFoundComponent, ClxPageServerErrorComponent, ClxPageUnauthorizedComponent, ClxPaginationComponent, ClxProductComponent, ClxProductDetailComponent, ClxProductQuickViewComponent, ClxProfileComponent, ClxProgressBarComponent, ClxRadioComponent, ClxRadioGroupComponent, ClxRatingComponent, ClxSearchComponent, ClxSelectComponent, ClxSkeletonComponent, ClxSliderComponent, ClxSocialIconComponent, ClxSpinnerComponent, ClxStatCardComponent, ClxStepComponent, ClxStepperComponent, ClxSwitchComponent, ClxTabDirective, ClxTableActionsComponent, ClxTableComponent, ClxTabsComponent, ClxTagComponent, ClxTextareaComponent, ClxThemeService, ClxTimelineComponent, ClxTimelineItemComponent, ClxTimepickerComponent, ClxToastComponent, ClxToastContainerComponent, ClxToastService, ClxTooltipComponent, ClxTooltipDirective, ClxTreeComponent, ClxUploadComponent, ClxWishlistComponent, ClxWizardComponent, TIMEPICKER_SIZE_MAP, parseColorInput, provideCodexlyTheme, resolveColor, resolveContainerRadius, resolveRadius };
|
|
17472
|
+
export { CLX_ADDON_BORDER, CLX_ADDON_TEXT, CLX_ALERT_OPTIONS, CLX_ALERT_RESOLVE, CLX_BG_ADDON, CLX_BG_DISABLED, CLX_BG_ICON_WRAP, CLX_BG_SECTION, CLX_BG_SURFACE, CLX_BORDER_DEFAULT, CLX_BORDER_DISABLED, CLX_BORDER_MEDIUM, CLX_COLOR_HEX, CLX_COLOR_HEX_100, CLX_COLOR_MAP, CLX_FONT_CATALOG, CLX_MODAL_ANIM_CONFIG, CLX_MODAL_DATA, CLX_MODAL_REF, CLX_OPTION_DISABLED, CLX_PLACEHOLDER, CLX_RADIO_GROUP, CLX_RADIUS_MAP, CLX_TEXT_BODY, CLX_TEXT_DISABLED, CLX_TEXT_HEADING, CLX_TEXT_HINT, CLX_TEXT_IDLE, CLX_TEXT_INPUT, CLX_TEXT_LABEL, CLX_TEXT_OPTION, CLX_TEXT_SUBTITLE, CLX_TEXT_TITLE, CLX_THEME_CONFIG, CLX_THEME_DEFAULTS, CLX_TOAST_DEFAULTS, ClxAlertComponent, ClxAlertService, ClxAnimateDirective, ClxAnimateGroupDirective, ClxAnimateService, ClxAppLayoutComponent, ClxAvatarComponent, ClxBadgeComponent, ClxBrandComponent, ClxButtonComponent, ClxButtonGroupComponent, ClxCardBodyDirective, ClxCardComponent, ClxCardFooterDirective, ClxCardHeaderActionsDirective, ClxCardHeaderDirective, ClxCarouselComponent, ClxCarouselDirective, ClxCartComponent, ClxCartSummaryDrawer, ClxCellDirective, ClxCheckboxComponent, ClxCollapseComponent, ClxColorPickerComponent, ClxColumnDefDirective, ClxDateRangePickerComponent, ClxDatepickerComponent, ClxDrawerComponent, ClxDrawerService, ClxEditorComponent, ClxEditorLinkModalComponent, ClxFabComponent, ClxFilterPanelComponent, ClxHeaderCellDirective, ClxIconComponent, ClxInputComponent, ClxListComponent, ClxListItemComponent, ClxMenuComponent, ClxMenuItemComponent, ClxMenuItemTrailingDirective, ClxModalComponent, ClxModalService, ClxNativeOverlayService, ClxNavGroupComponent, ClxNotificationComponent, ClxNumberComponent, ClxOtpComponent, ClxPageEmptyComponent, ClxPageHeaderComponent, ClxPageHeaderTitleDirective, ClxPageNotFoundComponent, ClxPageServerErrorComponent, ClxPageUnauthorizedComponent, ClxPaginationComponent, ClxProductComponent, ClxProductDetailComponent, ClxProductQuickViewComponent, ClxProfileComponent, ClxProgressBarComponent, ClxRadioComponent, ClxRadioGroupComponent, ClxRatingComponent, ClxSearchComponent, ClxSelectComponent, ClxSkeletonComponent, ClxSliderComponent, ClxSocialIconComponent, ClxSpinnerComponent, ClxStatCardComponent, ClxStepComponent, ClxStepperComponent, ClxSwitchComponent, ClxTabDirective, ClxTableActionsComponent, ClxTableComponent, ClxTabsComponent, ClxTagComponent, ClxTextareaComponent, ClxThemeService, ClxTimelineComponent, ClxTimelineItemComponent, ClxTimepickerComponent, ClxToastComponent, ClxToastContainerComponent, ClxToastService, ClxTooltipComponent, ClxTooltipDirective, ClxTreeComponent, ClxUploadComponent, ClxWishlistComponent, ClxWizardComponent, TIMEPICKER_SIZE_MAP, parseColorInput, provideCodexlyTheme, resolveColor, resolveContainerRadius, resolveRadius };
|
|
17069
17473
|
//# sourceMappingURL=codexly-ui.mjs.map
|