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