codexly-ui 0.11.9 → 0.12.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.
@@ -12096,33 +12096,85 @@ const TOAST_DEFAULT_ICON = {
12096
12096
  white: 'info',
12097
12097
  };
12098
12098
  const c = CLX_COLOR_MAP;
12099
- // ─── Border color per type ─────────────────────────────────────────────────────
12099
+ // ─── Card background (tinted surface) per type ────────────────────────────────
12100
+ const TOAST_BG_MAP = {
12101
+ red: c.red.bgSubtle,
12102
+ orange: c.orange.bgSubtle,
12103
+ amber: c.amber.bgSubtle,
12104
+ yellow: c.yellow.bgSubtle,
12105
+ lime: c.lime.bgSubtle,
12106
+ green: c.green.bgSubtle,
12107
+ emerald: c.emerald.bgSubtle,
12108
+ teal: c.teal.bgSubtle,
12109
+ cyan: c.cyan.bgSubtle,
12110
+ sky: c.sky.bgSubtle,
12111
+ blue: c.blue.bgSubtle,
12112
+ indigo: c.indigo.bgSubtle,
12113
+ violet: c.violet.bgSubtle,
12114
+ purple: c.purple.bgSubtle,
12115
+ fuchsia: c.fuchsia.bgSubtle,
12116
+ pink: c.pink.bgSubtle,
12117
+ rose: c.rose.bgSubtle,
12118
+ slate: c.slate.bgSubtle,
12119
+ gray: c.gray.bgSubtle,
12120
+ zinc: c.zinc.bgSubtle,
12121
+ neutral: c.neutral.bgSubtle,
12122
+ stone: c.stone.bgSubtle,
12123
+ white: c.white.bgSubtle,
12124
+ };
12125
+ // ─── Card border color per type ───────────────────────────────────────────────
12100
12126
  const TOAST_BORDER_MAP = {
12101
- red: c.red.borderBase,
12102
- orange: c.orange.borderBase,
12103
- amber: c.amber.borderBase,
12104
- yellow: c.yellow.borderBase,
12105
- lime: c.lime.borderBase,
12106
- green: c.green.borderBase,
12107
- emerald: c.emerald.borderBase,
12108
- teal: c.teal.borderBase,
12109
- cyan: c.cyan.borderBase,
12110
- sky: c.sky.borderBase,
12111
- blue: c.blue.borderBase,
12112
- indigo: c.indigo.borderBase,
12113
- violet: c.violet.borderBase,
12114
- purple: c.purple.borderBase,
12115
- fuchsia: c.fuchsia.borderBase,
12116
- pink: c.pink.borderBase,
12117
- rose: c.rose.borderBase,
12118
- slate: c.slate.borderBase,
12119
- gray: c.gray.borderBase,
12120
- zinc: c.zinc.borderBase,
12121
- neutral: c.neutral.borderBase,
12122
- stone: c.stone.borderBase,
12123
- white: c.white.borderBase,
12127
+ red: c.red.borderLight,
12128
+ orange: c.orange.borderLight,
12129
+ amber: c.amber.borderLight,
12130
+ yellow: c.yellow.borderLight,
12131
+ lime: c.lime.borderLight,
12132
+ green: c.green.borderLight,
12133
+ emerald: c.emerald.borderLight,
12134
+ teal: c.teal.borderLight,
12135
+ cyan: c.cyan.borderLight,
12136
+ sky: c.sky.borderLight,
12137
+ blue: c.blue.borderLight,
12138
+ indigo: c.indigo.borderLight,
12139
+ violet: c.violet.borderLight,
12140
+ purple: c.purple.borderLight,
12141
+ fuchsia: c.fuchsia.borderLight,
12142
+ pink: c.pink.borderLight,
12143
+ rose: c.rose.borderLight,
12144
+ slate: c.slate.borderLight,
12145
+ gray: c.gray.borderLight,
12146
+ zinc: c.zinc.borderLight,
12147
+ neutral: c.neutral.borderLight,
12148
+ stone: c.stone.borderLight,
12149
+ white: c.white.borderLight,
12150
+ };
12151
+ // ─── Icon disc background per type (solid, paired with white icon glyph) ─────
12152
+ const TOAST_ICON_BG_MAP = {
12153
+ red: c.red.bgBase,
12154
+ orange: c.orange.bgBase,
12155
+ amber: c.amber.bgBase,
12156
+ yellow: c.yellow.bgBase,
12157
+ lime: c.lime.bgBase,
12158
+ green: c.green.bgBase,
12159
+ emerald: c.emerald.bgBase,
12160
+ teal: c.teal.bgBase,
12161
+ cyan: c.cyan.bgBase,
12162
+ sky: c.sky.bgBase,
12163
+ blue: c.blue.bgBase,
12164
+ indigo: c.indigo.bgBase,
12165
+ violet: c.violet.bgBase,
12166
+ purple: c.purple.bgBase,
12167
+ fuchsia: c.fuchsia.bgBase,
12168
+ pink: c.pink.bgBase,
12169
+ rose: c.rose.bgBase,
12170
+ slate: c.slate.bgBase,
12171
+ gray: c.gray.bgBase,
12172
+ zinc: c.zinc.bgBase,
12173
+ neutral: c.neutral.bgBase,
12174
+ stone: c.stone.bgBase,
12175
+ white: c.white.bgBase,
12124
12176
  };
12125
- // ─── Icon / title text color per type ─────────────────────────────────────────
12177
+ // ─── Title / accent text color per type ───────────────────────────────────────
12126
12178
  const TOAST_COLOR_MAP = {
12127
12179
  red: c.red.textBase,
12128
12180
  orange: c.orange.textBase,
@@ -12201,11 +12253,29 @@ class ClxToastComponent {
12201
12253
  _destroyed = false;
12202
12254
  // ─── Derived visuals ──────────────────────────────────────────────────────
12203
12255
  _icon = computed(() => this.entry().icon ?? TOAST_DEFAULT_ICON[this.entry().type], ...(ngDevMode ? [{ debugName: "_icon" }] : /* istanbul ignore next */ []));
12256
+ _bgClass = computed(() => TOAST_BG_MAP[this.entry().type], ...(ngDevMode ? [{ debugName: "_bgClass" }] : /* istanbul ignore next */ []));
12204
12257
  _borderClass = computed(() => TOAST_BORDER_MAP[this.entry().type], ...(ngDevMode ? [{ debugName: "_borderClass" }] : /* istanbul ignore next */ []));
12258
+ _iconBgClass = computed(() => TOAST_ICON_BG_MAP[this.entry().type], ...(ngDevMode ? [{ debugName: "_iconBgClass" }] : /* istanbul ignore next */ []));
12205
12259
  _colorClass = computed(() => TOAST_COLOR_MAP[this.entry().type], ...(ngDevMode ? [{ debugName: "_colorClass" }] : /* istanbul ignore next */ []));
12206
12260
  _progressColor = computed(() => TOAST_PROGRESS_MAP[this.entry().type], ...(ngDevMode ? [{ debugName: "_progressColor" }] : /* istanbul ignore next */ []));
12207
12261
  _buttonColor = computed(() => this.entry().type, ...(ngDevMode ? [{ debugName: "_buttonColor" }] : /* istanbul ignore next */ []));
12208
12262
  _actions = computed(() => this.entry().actions ?? (this.entry().action ? [this.entry().action] : []), ...(ngDevMode ? [{ debugName: "_actions" }] : /* istanbul ignore next */ []));
12263
+ _relativeTime = computed(() => {
12264
+ const ts = this.entry().timestamp;
12265
+ if (!ts)
12266
+ return '';
12267
+ const diffMs = Date.now() - ts.getTime();
12268
+ const min = Math.round(diffMs / 60000);
12269
+ if (min < 1)
12270
+ return 'ahora';
12271
+ if (min < 60)
12272
+ return `hace ${min} min`;
12273
+ const hr = Math.round(min / 60);
12274
+ if (hr < 24)
12275
+ return `hace ${hr} h`;
12276
+ const day = Math.round(hr / 24);
12277
+ return `hace ${day} d`;
12278
+ }, ...(ngDevMode ? [{ debugName: "_relativeTime" }] : /* istanbul ignore next */ []));
12209
12279
  get _card() {
12210
12280
  return this._el.nativeElement.querySelector('.clx-toast-card');
12211
12281
  }
@@ -12282,40 +12352,57 @@ class ClxToastComponent {
12282
12352
  <!-- Toast card -->
12283
12353
  <div
12284
12354
  #card
12285
- class="clx-toast-card relative flex items-start gap-3 w-full bg-clx-surface backdrop-blur-md shadow-lg rounded-lg border-2 px-4 py-3.5 overflow-hidden"
12286
- [class]="_borderClass()"
12355
+ class="clx-toast-card relative flex items-start gap-3.5 w-full backdrop-blur-md shadow-lg rounded-xl border px-4 py-3.5 overflow-hidden"
12356
+ [class]="_bgClass() + ' ' + _borderClass()"
12287
12357
  (mouseenter)="_onMouseEnter()"
12288
12358
  (mouseleave)="_onMouseLeave()"
12289
12359
  >
12290
- <!-- Icon -->
12360
+ <!-- Icon disc -->
12291
12361
  <span
12292
- clx-icon
12293
- [name]="_icon()"
12294
- size="sm"
12295
- class="shrink-0 mt-0.5"
12296
- [class]="_colorClass()"
12297
- ></span>
12362
+ class="shrink-0 w-8 h-8 rounded-full flex items-center justify-center"
12363
+ [class]="_iconBgClass()"
12364
+ >
12365
+ <span clx-icon [name]="_icon()" size="sm" class="text-white"></span>
12366
+ </span>
12298
12367
 
12299
12368
  <!-- Content -->
12300
- <div class="flex-1 min-w-0">
12369
+ <div class="flex-1 min-w-0 pt-0.5">
12370
+ @if (entry().category) {
12371
+ <div class="flex items-center gap-1.5 mb-0.5">
12372
+ <span class="text-[11px] font-bold uppercase tracking-wide" [class]="_colorClass()">
12373
+ {{ entry().category }}
12374
+ </span>
12375
+ @if (entry().timestamp) {
12376
+ <span class="w-0.5 h-0.5 rounded-full bg-clx-text-subtle opacity-60"></span>
12377
+ <span class="text-[11px] text-clx-text-subtle">{{ _relativeTime() }}</span>
12378
+ }
12379
+ </div>
12380
+ }
12301
12381
  @if (entry().title) {
12302
- <p class="text-sm font-semibold leading-snug mb-0.5" [class]="_colorClass()">
12382
+ <p class="text-sm font-semibold leading-snug mb-0.5 flex items-center gap-1.5 flex-wrap" [class]="_colorClass()">
12303
12383
  {{ entry().title }}
12384
+ @if (entry().badgeLabel) {
12385
+ <span
12386
+ class="text-[10px] font-bold uppercase tracking-wide px-1.5 py-0.5 rounded text-white"
12387
+ [class]="_iconBgClass()"
12388
+ >
12389
+ {{ entry().badgeLabel }}
12390
+ </span>
12391
+ }
12304
12392
  </p>
12305
12393
  }
12306
- <p class="text-sm text-clx-text-muted leading-snug wrap-break-word">
12394
+ <p class="text-sm text-clx-text leading-snug wrap-break-word opacity-80">
12307
12395
  {{ entry().message }}
12308
12396
  </p>
12309
12397
  @if (_actions().length) {
12310
- <div class="flex items-center gap-3 mt-2">
12311
- @for (a of _actions(); track a.label) {
12398
+ <div class="flex items-center gap-2 mt-2.5">
12399
+ @for (a of _actions(); track a.label; let first = $first) {
12312
12400
  <button
12313
12401
  clx-button
12314
12402
  type="button"
12315
- [variant]="a.variant ?? 'link'"
12403
+ [variant]="(a.variant ?? (first ? 'primary' : 'ghost')) === 'primary' ? 'solid' : 'ghost'"
12316
12404
  [color]="_buttonColor()"
12317
12405
  size="xs"
12318
- [class]="a.variant && a.variant !== 'link' ? '' : '-ml-1 h-auto p-0'"
12319
12406
  (click)="_onAction(a)"
12320
12407
  >
12321
12408
  {{ a.label }}
@@ -12340,9 +12427,9 @@ class ClxToastComponent {
12340
12427
 
12341
12428
  <!-- Progress bar -->
12342
12429
  @if (entry().showProgress && entry().duration > 0) {
12343
- <div class="absolute bottom-0 left-0 right-0 h-0.5 bg-clx-surface-2">
12430
+ <div class="absolute bottom-0 left-px right-px h-0.5 overflow-hidden rounded-b-[11px]">
12344
12431
  <div
12345
- class="h-full w-full origin-left transition-none"
12432
+ class="h-full w-full origin-left transition-none opacity-45"
12346
12433
  [class]="_progressColor()"
12347
12434
  [ngStyle]="{ transform: 'scaleX(' + (_progressPct() / 100) + ')' }"
12348
12435
  ></div>
@@ -12357,40 +12444,57 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
12357
12444
  <!-- Toast card -->
12358
12445
  <div
12359
12446
  #card
12360
- class="clx-toast-card relative flex items-start gap-3 w-full bg-clx-surface backdrop-blur-md shadow-lg rounded-lg border-2 px-4 py-3.5 overflow-hidden"
12361
- [class]="_borderClass()"
12447
+ class="clx-toast-card relative flex items-start gap-3.5 w-full backdrop-blur-md shadow-lg rounded-xl border px-4 py-3.5 overflow-hidden"
12448
+ [class]="_bgClass() + ' ' + _borderClass()"
12362
12449
  (mouseenter)="_onMouseEnter()"
12363
12450
  (mouseleave)="_onMouseLeave()"
12364
12451
  >
12365
- <!-- Icon -->
12452
+ <!-- Icon disc -->
12366
12453
  <span
12367
- clx-icon
12368
- [name]="_icon()"
12369
- size="sm"
12370
- class="shrink-0 mt-0.5"
12371
- [class]="_colorClass()"
12372
- ></span>
12454
+ class="shrink-0 w-8 h-8 rounded-full flex items-center justify-center"
12455
+ [class]="_iconBgClass()"
12456
+ >
12457
+ <span clx-icon [name]="_icon()" size="sm" class="text-white"></span>
12458
+ </span>
12373
12459
 
12374
12460
  <!-- Content -->
12375
- <div class="flex-1 min-w-0">
12461
+ <div class="flex-1 min-w-0 pt-0.5">
12462
+ @if (entry().category) {
12463
+ <div class="flex items-center gap-1.5 mb-0.5">
12464
+ <span class="text-[11px] font-bold uppercase tracking-wide" [class]="_colorClass()">
12465
+ {{ entry().category }}
12466
+ </span>
12467
+ @if (entry().timestamp) {
12468
+ <span class="w-0.5 h-0.5 rounded-full bg-clx-text-subtle opacity-60"></span>
12469
+ <span class="text-[11px] text-clx-text-subtle">{{ _relativeTime() }}</span>
12470
+ }
12471
+ </div>
12472
+ }
12376
12473
  @if (entry().title) {
12377
- <p class="text-sm font-semibold leading-snug mb-0.5" [class]="_colorClass()">
12474
+ <p class="text-sm font-semibold leading-snug mb-0.5 flex items-center gap-1.5 flex-wrap" [class]="_colorClass()">
12378
12475
  {{ entry().title }}
12476
+ @if (entry().badgeLabel) {
12477
+ <span
12478
+ class="text-[10px] font-bold uppercase tracking-wide px-1.5 py-0.5 rounded text-white"
12479
+ [class]="_iconBgClass()"
12480
+ >
12481
+ {{ entry().badgeLabel }}
12482
+ </span>
12483
+ }
12379
12484
  </p>
12380
12485
  }
12381
- <p class="text-sm text-clx-text-muted leading-snug wrap-break-word">
12486
+ <p class="text-sm text-clx-text leading-snug wrap-break-word opacity-80">
12382
12487
  {{ entry().message }}
12383
12488
  </p>
12384
12489
  @if (_actions().length) {
12385
- <div class="flex items-center gap-3 mt-2">
12386
- @for (a of _actions(); track a.label) {
12490
+ <div class="flex items-center gap-2 mt-2.5">
12491
+ @for (a of _actions(); track a.label; let first = $first) {
12387
12492
  <button
12388
12493
  clx-button
12389
12494
  type="button"
12390
- [variant]="a.variant ?? 'link'"
12495
+ [variant]="(a.variant ?? (first ? 'primary' : 'ghost')) === 'primary' ? 'solid' : 'ghost'"
12391
12496
  [color]="_buttonColor()"
12392
12497
  size="xs"
12393
- [class]="a.variant && a.variant !== 'link' ? '' : '-ml-1 h-auto p-0'"
12394
12498
  (click)="_onAction(a)"
12395
12499
  >
12396
12500
  {{ a.label }}
@@ -12415,9 +12519,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
12415
12519
 
12416
12520
  <!-- Progress bar -->
12417
12521
  @if (entry().showProgress && entry().duration > 0) {
12418
- <div class="absolute bottom-0 left-0 right-0 h-0.5 bg-clx-surface-2">
12522
+ <div class="absolute bottom-0 left-px right-px h-0.5 overflow-hidden rounded-b-[11px]">
12419
12523
  <div
12420
- class="h-full w-full origin-left transition-none"
12524
+ class="h-full w-full origin-left transition-none opacity-45"
12421
12525
  [class]="_progressColor()"
12422
12526
  [ngStyle]="{ transform: 'scaleX(' + (_progressPct() / 100) + ')' }"
12423
12527
  ></div>
@@ -17410,6 +17514,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
17410
17514
  args: [{ providedIn: 'root' }]
17411
17515
  }] });
17412
17516
 
17517
+ const NOTIFICATION_SEVERITY_TO_TOAST_TYPE = {
17518
+ success: 'green',
17519
+ warning: 'amber',
17520
+ info: 'sky',
17521
+ error: 'red',
17522
+ };
17413
17523
  class ClxToastService {
17414
17524
  _appRef = inject(ApplicationRef);
17415
17525
  _injector = inject(Injector);
@@ -17430,6 +17540,9 @@ class ClxToastService {
17430
17540
  message: options.message,
17431
17541
  title: options.title,
17432
17542
  icon: options.icon,
17543
+ category: options.category,
17544
+ timestamp: options.timestamp,
17545
+ badgeLabel: options.badgeLabel,
17433
17546
  position,
17434
17547
  duration: options.duration ?? TOAST_DEFAULTS.duration,
17435
17548
  showProgress: options.showProgress ?? TOAST_DEFAULTS.showProgress,
@@ -17457,6 +17570,19 @@ class ClxToastService {
17457
17570
  info(message, opts = {}) {
17458
17571
  this.show({ ...opts, type: 'sky', message });
17459
17572
  }
17573
+ /** Shows a toast built straight from a live ClxNotificationItem (e.g. on SSE arrival) — the same
17574
+ * object already driving the notification panel/bell, no manual field mapping at the call site. */
17575
+ fromNotification(item, opts = {}) {
17576
+ this.show({
17577
+ ...opts,
17578
+ type: NOTIFICATION_SEVERITY_TO_TOAST_TYPE[item.severity],
17579
+ icon: item.icon,
17580
+ title: item.title,
17581
+ message: item.detail ?? '',
17582
+ category: item.category,
17583
+ timestamp: new Date(item.createdAt),
17584
+ });
17585
+ }
17460
17586
  dismissAll() {
17461
17587
  this._containers.forEach(c => c.instance.entries.set([]));
17462
17588
  }