@africanies/angular-web-sdk 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/.nx/cache/{4082520855419352840 → 15302120112746851896}/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +30 -8
  2. package/.nx/cache/15302120112746851896/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -0
  3. package/.nx/cache/{4082520855419352840 → 15302120112746851896}/dist/libs/africanies-theme/tailwind-preset.cjs +89 -12
  4. package/.nx/cache/{4082520855419352840 → 15302120112746851896}/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +14 -4
  5. package/.nx/cache/{17491371155400585077 → 18393832731522001121}/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +139 -51
  6. package/.nx/cache/18393832731522001121/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +1 -0
  7. package/.nx/cache/{17491371155400585077 → 18393832731522001121}/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +14 -11
  8. package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/README.md +69 -0
  9. package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/assets/icons.sprite.svg +1851 -0
  10. package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs +833 -0
  11. package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs.map +1 -0
  12. package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/package.json +30 -0
  13. package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/types/africanies-africanies-icons.d.ts +108 -0
  14. package/.nx/cache/877026836685850193/dist/libs/africanies-models/README.md +60 -0
  15. package/.nx/cache/877026836685850193/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs +613 -0
  16. package/.nx/cache/877026836685850193/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs.map +1 -0
  17. package/.nx/cache/877026836685850193/dist/libs/africanies-models/package.json +23 -0
  18. package/.nx/cache/877026836685850193/dist/libs/africanies-models/types/africanies-africanies-models.d.ts +1452 -0
  19. package/.nx/cache/{17499961385238663835 → 9406544675747598148}/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +765 -615
  20. package/.nx/cache/9406544675747598148/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -0
  21. package/.nx/cache/{17499961385238663835 → 9406544675747598148}/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +149 -116
  22. package/.nx/cache/run.json +40 -40
  23. package/.nx/cache/terminalOutputs/{7126777490381568487 → 15149466893522345489} +10 -5
  24. package/.nx/cache/terminalOutputs/{4082520855419352840 → 15302120112746851896} +1 -1
  25. package/.nx/cache/terminalOutputs/{8648260256613557249 → 15835338512221847842} +18 -8
  26. package/.nx/cache/terminalOutputs/{9408529484792607601 → 17960163310445607219} +1 -1
  27. package/.nx/cache/terminalOutputs/{17491371155400585077 → 18393832731522001121} +2 -2
  28. package/.nx/cache/terminalOutputs/{3314955264390318442 → 5151121439688073678} +6 -6
  29. package/.nx/cache/terminalOutputs/7462873189468732296 +17 -0
  30. package/.nx/cache/terminalOutputs/8032148447904822353 +184 -182
  31. package/.nx/cache/terminalOutputs/{12276756491762927325 → 877026836685850193} +7 -7
  32. package/.nx/cache/terminalOutputs/9406544675747598148 +21 -0
  33. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db +0 -0
  34. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-shm +0 -0
  35. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-wal +0 -0
  36. package/.nx/workspace-data/d/daemon.log +2353 -0
  37. package/.nx/workspace-data/d/server-process.json +2 -2
  38. package/.nx/workspace-data/file-map.json +1802 -1736
  39. package/.nx/workspace-data/nx_files.nxt +0 -0
  40. package/.nx/workspace-data/project-graph.json +7 -2
  41. package/apps/playground/src/app/pages/tokens-page.ts +17 -13
  42. package/apps/playground/src/app/snippets/filters.snippets.ts +10 -0
  43. package/dist/libs/africanies-icons/assets/icons.sprite.svg +7 -0
  44. package/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs +1 -0
  45. package/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs.map +1 -1
  46. package/dist/libs/africanies-icons/types/africanies-africanies-icons.d.ts +2 -2
  47. package/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs +22 -8
  48. package/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs.map +1 -1
  49. package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +22 -10
  50. package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -1
  51. package/dist/libs/africanies-theme/tailwind-preset.cjs +84 -16
  52. package/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +4 -2
  53. package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +555 -425
  54. package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -1
  55. package/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +140 -110
  56. package/libs/africanies-icons/src/assets/icons.sprite.svg +7 -0
  57. package/libs/africanies-icons/src/lib/icon-name.ts +1 -0
  58. package/libs/africanies-models/src/lib/filters/configs/seed-configs.ts +22 -8
  59. package/libs/africanies-theme/src/lib/mode-color.safelist.ts +12 -0
  60. package/libs/africanies-theme/src/lib/mode-color.service.spec.ts +55 -0
  61. package/libs/africanies-theme/src/lib/mode-color.service.ts +11 -11
  62. package/libs/africanies-theme/tailwind-preset.cjs +84 -16
  63. package/libs/africanies-ui/src/lib/action-menu/action-menu.component.ts +3 -3
  64. package/libs/africanies-ui/src/lib/alert/alert.component.ts +4 -4
  65. package/libs/africanies-ui/src/lib/avatar/avatar-menu.component.ts +2 -2
  66. package/libs/africanies-ui/src/lib/button/button.component.spec.ts +7 -7
  67. package/libs/africanies-ui/src/lib/button/button.component.ts +5 -5
  68. package/libs/africanies-ui/src/lib/chip/chip.component.ts +4 -4
  69. package/libs/africanies-ui/src/lib/content-stack/content-stack.component.ts +1 -1
  70. package/libs/africanies-ui/src/lib/feedback/error-indicator.component.ts +3 -3
  71. package/libs/africanies-ui/src/lib/feedback/error-state.component.ts +2 -2
  72. package/libs/africanies-ui/src/lib/filters/filter-drawer.panel.ts +44 -27
  73. package/libs/africanies-ui/src/lib/forms/checkbox/checkbox.component.ts +2 -1
  74. package/libs/africanies-ui/src/lib/forms/file-upload/file-preview.dialog.ts +1 -1
  75. package/libs/africanies-ui/src/lib/forms/file-upload/file-upload.component.ts +1 -1
  76. package/libs/africanies-ui/src/lib/forms/form-field.classes.ts +36 -6
  77. package/libs/africanies-ui/src/lib/forms/otp-input/otp-input.component.ts +2 -1
  78. package/libs/africanies-ui/src/lib/forms/radio/radio.component.ts +2 -1
  79. package/libs/africanies-ui/src/lib/forms/select/select.component.ts +1 -1
  80. package/libs/africanies-ui/src/lib/forms/toggle/toggle.component.ts +1 -1
  81. package/libs/africanies-ui/src/lib/image/image.component.ts +1 -1
  82. package/libs/africanies-ui/src/lib/info-popover/info-popover.component.ts +2 -2
  83. package/libs/africanies-ui/src/lib/layout/app-shell-content-header.component.ts +1 -1
  84. package/libs/africanies-ui/src/lib/layout/app-shell-header.component.ts +5 -5
  85. package/libs/africanies-ui/src/lib/layout/app-shell.component.html +2 -2
  86. package/libs/africanies-ui/src/lib/layout/app-shell.component.ts +3 -3
  87. package/libs/africanies-ui/src/lib/navigation/shipping-mode-switch/shipping-mode-switch.component.ts +6 -6
  88. package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.html +21 -13
  89. package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.ts +13 -7
  90. package/libs/africanies-ui/src/lib/notifications/notification-drawer.panel.ts +6 -6
  91. package/libs/africanies-ui/src/lib/overlay/confirm-dialog.component.ts +2 -2
  92. package/libs/africanies-ui/src/lib/overlay/drawer.service.ts +2 -2
  93. package/libs/africanies-ui/src/lib/overlay/modal.service.ts +3 -3
  94. package/libs/africanies-ui/src/lib/overlay/overlay-frame.component.ts +1 -1
  95. package/libs/africanies-ui/src/lib/pagination/pagination.component.ts +1 -1
  96. package/libs/africanies-ui/src/lib/stepper/stepper.component.ts +7 -7
  97. package/libs/africanies-ui/src/lib/table/table-column.ts +9 -0
  98. package/libs/africanies-ui/src/lib/table/table.component.spec.ts +99 -0
  99. package/libs/africanies-ui/src/lib/table/table.component.ts +72 -30
  100. package/libs/africanies-ui/src/lib/toast/toast-item.component.ts +6 -6
  101. package/package.json +1 -1
  102. package/svg/hand-coins.svg +9 -0
  103. package/.nx/cache/12276756491762927325/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +0 -215
  104. package/.nx/cache/12276756491762927325/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +0 -1
  105. package/.nx/cache/12276756491762927325/dist/libs/africanies-theme/tailwind-preset.cjs +0 -245
  106. package/.nx/cache/12276756491762927325/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +0 -157
  107. package/.nx/cache/17491371155400585077/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +0 -1
  108. package/.nx/cache/17499961385238663835/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +0 -1
  109. package/.nx/cache/3314955264390318442/dist/libs/africanies-ui/README.md +0 -68
  110. package/.nx/cache/3314955264390318442/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
  111. package/.nx/cache/3314955264390318442/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
  112. package/.nx/cache/3314955264390318442/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +0 -5
  113. package/.nx/cache/3314955264390318442/dist/libs/africanies-ui/assets/carriers/dhl.svg +0 -7
  114. package/.nx/cache/3314955264390318442/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +0 -19291
  115. package/.nx/cache/3314955264390318442/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +0 -1
  116. package/.nx/cache/3314955264390318442/dist/libs/africanies-ui/package.json +0 -35
  117. package/.nx/cache/3314955264390318442/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +0 -5483
  118. package/.nx/cache/4082520855419352840/dist/libs/africanies-theme/README.md +0 -46
  119. package/.nx/cache/4082520855419352840/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +0 -1
  120. package/.nx/cache/4082520855419352840/dist/libs/africanies-theme/package.json +0 -35
  121. package/.nx/cache/terminalOutputs/17499961385238663835 +0 -21
  122. package/.nx/cache/terminalOutputs/3017748574492080228 +0 -28
  123. /package/.nx/cache/{12276756491762927325 → 15302120112746851896}/dist/libs/africanies-theme/README.md +0 -0
  124. /package/.nx/cache/{12276756491762927325 → 15302120112746851896}/dist/libs/africanies-theme/package.json +0 -0
  125. /package/.nx/cache/{17491371155400585077 → 18393832731522001121}/dist/libs/africanies-core/README.md +0 -0
  126. /package/.nx/cache/{17491371155400585077 → 18393832731522001121}/dist/libs/africanies-core/package.json +0 -0
  127. /package/.nx/cache/{17499961385238663835 → 9406544675747598148}/dist/libs/africanies-ui/README.md +0 -0
  128. /package/.nx/cache/{17499961385238663835 → 9406544675747598148}/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
  129. /package/.nx/cache/{17499961385238663835 → 9406544675747598148}/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
  130. /package/.nx/cache/{17499961385238663835 → 9406544675747598148}/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +0 -0
  131. /package/.nx/cache/{17499961385238663835 → 9406544675747598148}/dist/libs/africanies-ui/assets/carriers/dhl.svg +0 -0
  132. /package/.nx/cache/{17499961385238663835 → 9406544675747598148}/dist/libs/africanies-ui/package.json +0 -0
  133. /package/.nx/cache/terminalOutputs/{16279119293212733518 → 14005735098959303198} +0 -0
@@ -92,7 +92,7 @@ class ButtonComponent {
92
92
  */
93
93
  hostClass = computed(() => {
94
94
  const base = 'relative inline-flex items-center justify-center gap-2 box-border font-sans font-medium rounded-md border transition-colors cursor-pointer ' +
95
- 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink ' +
95
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus ' +
96
96
  'disabled:opacity-50 disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:cursor-not-allowed';
97
97
  const sizes = {
98
98
  sm: 'text-body-sm px-2.5 py-0 h-8',
@@ -112,15 +112,15 @@ class ButtonComponent {
112
112
  }
113
113
  else if (variant === 'flat') {
114
114
  variantClass =
115
- 'bg-white dark:bg-ink-950 text-ink dark:text-white border-border dark:border-white/15 hover:bg-background-welcome dark:hover:bg-white/10';
115
+ 'bg-surface dark:bg-ink-950 text-ink dark:text-white border-control dark:border-white/15 hover:bg-background-hover dark:hover:bg-white/10';
116
116
  }
117
117
  else if (variant === 'ghost') {
118
118
  variantClass =
119
- 'bg-transparent text-ink dark:text-white border-transparent hover:bg-background-welcome dark:hover:bg-white/10';
119
+ 'bg-transparent text-ink dark:text-white border-transparent hover:bg-background-hover dark:hover:bg-white/10';
120
120
  }
121
121
  else if (variant === 'ghost-danger') {
122
122
  variantClass =
123
- 'bg-transparent text-danger border-transparent hover:bg-danger-subtle dark:hover:bg-danger/10';
123
+ 'bg-transparent text-danger-dark border-transparent hover:bg-danger-subtle dark:text-danger dark:hover:bg-danger/10';
124
124
  }
125
125
  else if (variant === 'underline') {
126
126
  variantClass =
@@ -128,7 +128,7 @@ class ButtonComponent {
128
128
  }
129
129
  else {
130
130
  variantClass =
131
- 'bg-danger text-white border-transparent hover:bg-danger-dark';
131
+ 'bg-danger-dark text-white border-transparent hover:bg-danger-strong dark:bg-danger dark:hover:bg-danger-dark';
132
132
  }
133
133
  return `${base} ${sizes[this.size()]} ${variantClass}`;
134
134
  }, /* @ts-ignore */
@@ -271,8 +271,8 @@ class ToastItemComponent {
271
271
  return v === 'warning' || v === 'danger' ? 'assertive' : 'polite';
272
272
  }, /* @ts-ignore */
273
273
  ...(ngDevMode ? [{ debugName: "a11yLive" }] : /* istanbul ignore next */ []));
274
- shellClass = computed(() => 'africanies-toast-enter relative flex w-full overflow-hidden rounded-lg border border-border ' +
275
- 'bg-white/95 text-ink shadow-lg backdrop-blur-md ' +
274
+ shellClass = computed(() => 'africanies-toast-enter relative flex w-full overflow-hidden rounded-panel border border-border ' +
275
+ 'bg-surface/95 text-ink shadow-floating backdrop-blur-md ' +
276
276
  'dark:border-white/15 dark:bg-ink-950/95 dark:text-white', /* @ts-ignore */
277
277
  ...(ngDevMode ? [{ debugName: "shellClass" }] : /* istanbul ignore next */ []));
278
278
  accentClass = computed(() => `w-1 shrink-0 self-stretch ${accentFill(this.item().variant)}`, /* @ts-ignore */
@@ -280,11 +280,11 @@ class ToastItemComponent {
280
280
  iconClass = computed(() => {
281
281
  switch (this.item().variant) {
282
282
  case 'success':
283
- return 'text-export dark:text-export-light';
283
+ return 'text-export-strong dark:text-export-light';
284
284
  case 'warning':
285
285
  return 'text-warning-dark dark:text-warning';
286
286
  case 'danger':
287
- return 'text-danger';
287
+ return 'text-danger-dark dark:text-danger';
288
288
  default:
289
289
  return 'text-ink-blue dark:text-neutral-300';
290
290
  }
@@ -294,11 +294,11 @@ class ToastItemComponent {
294
294
  const base = 'm-0 break-words text-body-sm font-semibold';
295
295
  switch (this.item().variant) {
296
296
  case 'success':
297
- return `${base} text-export dark:text-export-light`;
297
+ return `${base} text-export-strong dark:text-export-light`;
298
298
  case 'warning':
299
299
  return `${base} text-warning-dark dark:text-warning`;
300
300
  case 'danger':
301
- return `${base} text-danger`;
301
+ return `${base} text-danger-dark dark:text-danger`;
302
302
  default:
303
303
  return `${base} text-ink dark:text-white`;
304
304
  }
@@ -1504,9 +1504,9 @@ class ActionMenuComponent {
1504
1504
  const active = this.activeIndex() === index;
1505
1505
  const base = 'flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-body-sm no-underline transition-colors ' +
1506
1506
  'disabled:cursor-not-allowed disabled:opacity-50 ' +
1507
- 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ink';
1507
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus';
1508
1508
  if (item.danger) {
1509
- return (`${base} text-danger ` +
1509
+ return (`${base} text-danger-dark dark:text-danger ` +
1510
1510
  (active
1511
1511
  ? 'bg-danger-subtle dark:bg-danger/15'
1512
1512
  : 'hover:bg-danger-subtle dark:hover:bg-danger/15'));
@@ -1710,7 +1710,7 @@ class ActionMenuComponent {
1710
1710
  >
1711
1711
  <div
1712
1712
  #panel
1713
- class="min-w-44 rounded-md border border-border bg-white py-1 shadow-lg outline-none dark:border-white/15 dark:bg-ink-950"
1713
+ class="min-w-44 rounded-md border border-border bg-surface py-1 shadow-floating outline-none dark:border-white/15 dark:bg-ink-950"
1714
1714
  role="menu"
1715
1715
  tabindex="-1"
1716
1716
  [attr.aria-label]="ariaLabel()"
@@ -1827,7 +1827,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
1827
1827
  >
1828
1828
  <div
1829
1829
  #panel
1830
- class="min-w-44 rounded-md border border-border bg-white py-1 shadow-lg outline-none dark:border-white/15 dark:bg-ink-950"
1830
+ class="min-w-44 rounded-md border border-border bg-surface py-1 shadow-floating outline-none dark:border-white/15 dark:bg-ink-950"
1831
1831
  role="menu"
1832
1832
  tabindex="-1"
1833
1833
  [attr.aria-label]="ariaLabel()"
@@ -2056,10 +2056,10 @@ class ErrorIndicatorComponent {
2056
2056
  aria-hidden="true"
2057
2057
  >
2058
2058
  <span
2059
- class="africanies-error-indicator-dot-ping absolute inset-0 rounded-full bg-red-400 opacity-75"
2059
+ class="africanies-error-indicator-dot-ping absolute inset-0 rounded-full bg-danger-dark opacity-75"
2060
2060
  ></span>
2061
2061
  <span
2062
- class="africanies-error-indicator-dot-core relative size-3 rounded-full bg-red-500"
2062
+ class="africanies-error-indicator-dot-core relative size-3 rounded-full bg-danger-dark"
2063
2063
  ></span>
2064
2064
  </span>
2065
2065
 
@@ -2074,7 +2074,7 @@ class ErrorIndicatorComponent {
2074
2074
  type="button"
2075
2075
  variant="ghost"
2076
2076
  size="sm"
2077
- class="africanies-error-indicator-retry shrink-0 !min-h-0 !border-transparent !bg-transparent !px-1.5 !py-0.5 !text-danger hover:!bg-danger/10 dark:hover:!bg-danger/20"
2077
+ class="africanies-error-indicator-retry shrink-0 !min-h-0 !border-transparent !bg-transparent !px-1.5 !py-0.5 !text-danger-dark hover:!bg-danger/10 dark:hover:!bg-danger/20"
2078
2078
  [class.opacity-50]="disabled()"
2079
2079
  [disabled]="disabled()"
2080
2080
  (click)="onRetry()"
@@ -2101,10 +2101,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2101
2101
  aria-hidden="true"
2102
2102
  >
2103
2103
  <span
2104
- class="africanies-error-indicator-dot-ping absolute inset-0 rounded-full bg-red-400 opacity-75"
2104
+ class="africanies-error-indicator-dot-ping absolute inset-0 rounded-full bg-danger-dark opacity-75"
2105
2105
  ></span>
2106
2106
  <span
2107
- class="africanies-error-indicator-dot-core relative size-3 rounded-full bg-red-500"
2107
+ class="africanies-error-indicator-dot-core relative size-3 rounded-full bg-danger-dark"
2108
2108
  ></span>
2109
2109
  </span>
2110
2110
 
@@ -2119,7 +2119,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2119
2119
  type="button"
2120
2120
  variant="ghost"
2121
2121
  size="sm"
2122
- class="africanies-error-indicator-retry shrink-0 !min-h-0 !border-transparent !bg-transparent !px-1.5 !py-0.5 !text-danger hover:!bg-danger/10 dark:hover:!bg-danger/20"
2122
+ class="africanies-error-indicator-retry shrink-0 !min-h-0 !border-transparent !bg-transparent !px-1.5 !py-0.5 !text-danger-dark hover:!bg-danger/10 dark:hover:!bg-danger/20"
2123
2123
  [class.opacity-50]="disabled()"
2124
2124
  [disabled]="disabled()"
2125
2125
  (click)="onRetry()"
@@ -2170,8 +2170,8 @@ class ErrorStateComponent {
2170
2170
  class="flex flex-col items-center justify-center gap-3 py-10 px-4 text-center text-ink dark:text-white"
2171
2171
  role="alert"
2172
2172
  >
2173
- <africanies-icon name="warning" [size]="32" class="text-danger" />
2174
- <p class="m-0 text-body text-danger max-w-md">{{ message() }}</p>
2173
+ <africanies-icon name="warning" [size]="32" class="text-danger-dark dark:text-danger" />
2174
+ <p class="m-0 text-body text-danger-dark max-w-md dark:text-danger">{{ message() }}</p>
2175
2175
  <button
2176
2176
  africanies-button
2177
2177
  type="button"
@@ -2201,8 +2201,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2201
2201
  class="flex flex-col items-center justify-center gap-3 py-10 px-4 text-center text-ink dark:text-white"
2202
2202
  role="alert"
2203
2203
  >
2204
- <africanies-icon name="warning" [size]="32" class="text-danger" />
2205
- <p class="m-0 text-body text-danger max-w-md">{{ message() }}</p>
2204
+ <africanies-icon name="warning" [size]="32" class="text-danger-dark dark:text-danger" />
2205
+ <p class="m-0 text-body text-danger-dark max-w-md dark:text-danger">{{ message() }}</p>
2206
2206
  <button
2207
2207
  africanies-button
2208
2208
  type="button"
@@ -2739,11 +2739,11 @@ class AlertComponent {
2739
2739
  iconClass = computed(() => {
2740
2740
  switch (this.variant()) {
2741
2741
  case 'success':
2742
- return 'text-export dark:text-export-light';
2742
+ return 'text-export-strong dark:text-export-light';
2743
2743
  case 'warning':
2744
2744
  return 'text-warning-dark dark:text-warning';
2745
2745
  case 'danger':
2746
- return 'text-danger';
2746
+ return 'text-danger-dark dark:text-danger';
2747
2747
  default:
2748
2748
  return 'text-ink-blue dark:text-neutral-300';
2749
2749
  }
@@ -2753,11 +2753,11 @@ class AlertComponent {
2753
2753
  const base = 'm-0 text-body font-medium';
2754
2754
  switch (this.variant()) {
2755
2755
  case 'success':
2756
- return `${base} text-export dark:text-export-light`;
2756
+ return `${base} text-export-strong dark:text-export-light`;
2757
2757
  case 'warning':
2758
2758
  return `${base} text-warning-dark dark:text-warning`;
2759
2759
  case 'danger':
2760
- return `${base} text-danger`;
2760
+ return `${base} text-danger-dark dark:text-danger`;
2761
2761
  default:
2762
2762
  return `${base} text-ink dark:text-white`;
2763
2763
  }
@@ -2910,13 +2910,13 @@ class ChipComponent {
2910
2910
  switch (this.variant()) {
2911
2911
  case 'success':
2912
2912
  case 'export':
2913
- return 'bg-export-subtle text-export dark:bg-export/15 dark:text-export-light';
2913
+ return 'bg-export-subtle text-export-strong dark:bg-export/15 dark:text-export-light';
2914
2914
  case 'import':
2915
- return 'bg-import-subtle text-import dark:bg-import/15 dark:text-import-light';
2915
+ return 'bg-import-subtle text-import-strong dark:bg-import/15 dark:text-import-light';
2916
2916
  case 'warning':
2917
2917
  return 'bg-warning-subtle text-warning-dark dark:bg-warning/15 dark:text-warning';
2918
2918
  case 'danger':
2919
- return 'bg-danger-subtle text-danger dark:bg-danger/15 dark:text-danger';
2919
+ return 'bg-danger-subtle text-danger-dark dark:bg-danger/15 dark:text-danger';
2920
2920
  default:
2921
2921
  return 'bg-border/60 text-ink dark:bg-white/10 dark:text-neutral-200';
2922
2922
  }
@@ -2937,7 +2937,7 @@ class ChipComponent {
2937
2937
  @if (removable()) {
2938
2938
  <button
2939
2939
  type="button"
2940
- class="inline-flex shrink-0 cursor-pointer items-center justify-center rounded-sm opacity-70 transition-opacity hover:opacity-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-ink"
2940
+ class="inline-flex shrink-0 cursor-pointer items-center justify-center rounded-sm opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus"
2941
2941
  [attr.aria-label]="removeLabel()"
2942
2942
  (click)="onRemove($event)"
2943
2943
  >
@@ -2965,7 +2965,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2965
2965
  @if (removable()) {
2966
2966
  <button
2967
2967
  type="button"
2968
- class="inline-flex shrink-0 cursor-pointer items-center justify-center rounded-sm opacity-70 transition-opacity hover:opacity-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-ink"
2968
+ class="inline-flex shrink-0 cursor-pointer items-center justify-center rounded-sm opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus"
2969
2969
  [attr.aria-label]="removeLabel()"
2970
2970
  (click)="onRemove($event)"
2971
2971
  >
@@ -3180,7 +3180,7 @@ class ContentStackComponent {
3180
3180
  </p>
3181
3181
  }
3182
3182
  @if (extraLine()) {
3183
- <p class="m-0 text-caption text-neutral-500 dark:text-neutral-400">
3183
+ <p class="m-0 text-caption text-neutral-600 dark:text-neutral-400">
3184
3184
  {{ extraLine() }}
3185
3185
  </p>
3186
3186
  }
@@ -3209,7 +3209,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
3209
3209
  </p>
3210
3210
  }
3211
3211
  @if (extraLine()) {
3212
- <p class="m-0 text-caption text-neutral-500 dark:text-neutral-400">
3212
+ <p class="m-0 text-caption text-neutral-600 dark:text-neutral-400">
3213
3213
  {{ extraLine() }}
3214
3214
  </p>
3215
3215
  }
@@ -3782,8 +3782,8 @@ class InfoPopoverComponent {
3782
3782
  * Bound via `[class]` so arrow position tracks preferred placement.
3783
3783
  */
3784
3784
  panelArrowClass = computed(() => {
3785
- const card = 'relative z-10 w-max max-w-xs rounded-lg border border-border bg-white px-4 py-3 text-ink shadow-lg dark:border-white/15 dark:bg-ink-950 dark:text-white';
3786
- const base = "before:pointer-events-none before:absolute before:z-0 before:h-2.5 before:w-2.5 before:rotate-45 before:border before:border-border before:bg-white before:content-[''] dark:before:border-white/15 dark:before:bg-ink-950";
3785
+ const card = 'relative z-10 w-max max-w-xs rounded-panel border border-border bg-surface px-4 py-3 text-ink shadow-floating dark:border-white/15 dark:bg-ink-950 dark:text-white';
3786
+ const base = "before:pointer-events-none before:absolute before:z-0 before:h-2.5 before:w-2.5 before:rotate-45 before:border before:border-border before:bg-surface before:content-[''] dark:before:border-white/15 dark:before:bg-ink-950";
3787
3787
  switch (this.placement()) {
3788
3788
  case 'bottom':
3789
3789
  return `${card} ${base} before:left-1/2 before:top-0 before:-translate-x-1/2 before:-translate-y-1/2 before:border-b-0 before:border-r-0`;
@@ -4083,22 +4083,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
4083
4083
  * scanner must see full utility names in this package so they survive purge
4084
4084
  * when consumers scan the published UI bundle.
4085
4085
  */
4086
+ /**
4087
+ * Inset focus ring — stays inside the control border box.
4088
+ *
4089
+ * Prefer this over `outline` + `outline-offset`. Those paint *outside* the box
4090
+ * and are clipped by `overflow: hidden|clip|auto` ancestors (modals, drawers,
4091
+ * steppers, scroll panes). `box-sizing` does not affect outlines.
4092
+ */
4093
+ const FORM_FOCUS_WITHIN_CLASS = 'focus-within:outline-none focus-within:ring-2 focus-within:ring-inset focus-within:ring-focus';
4094
+ /** Same inset ring for host / button-style focus-visible. */
4095
+ const FORM_FOCUS_VISIBLE_CLASS = 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus';
4096
+ /** Peer-driven inset ring (checkbox / radio control chrome). */
4097
+ const FORM_FOCUS_VISIBLE_PEER_CLASS = 'peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-inset peer-focus-visible:ring-focus';
4098
+ /** Direct focus (OTP cells and similar). */
4099
+ const FORM_FOCUS_CLASS = 'focus:outline-none focus:ring-2 focus:ring-inset focus:ring-focus';
4086
4100
  /** Label above the control. Hidden when empty so callers can omit a label. */
4087
4101
  const FORM_LABEL_CLASS = 'block text-body-sm font-medium text-ink dark:text-white mb-1.5 empty:hidden';
4088
- /** Default control shell — `h-10` matches md `africanies-button`. */
4089
- const FORM_FIELD_CLASS = 'flex items-center w-full h-10 box-border rounded-md border border-border bg-white dark:bg-ink-950 text-ink dark:text-white dark:border-white/25 ' +
4090
- 'focus-within:outline focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-ink';
4102
+ /**
4103
+ * Default control shell `h-10` matches md `africanies-button`.
4104
+ *
4105
+ * Focus utilities are inlined (not concatenated) so Tailwind content scanning
4106
+ * always sees the full class names in this string literal.
4107
+ */
4108
+ const FORM_FIELD_CLASS = 'flex items-center w-full h-10 box-border rounded-control border border-control bg-surface dark:bg-ink-950 text-ink dark:text-white dark:border-white/25 ' +
4109
+ 'focus-within:outline-none focus-within:ring-2 focus-within:ring-inset focus-within:ring-focus';
4091
4110
  /**
4092
4111
  * Error border — `!` so it wins over shell `border-border` /
4093
4112
  * `dark:border-white/25` (same specificity; those utilities sort later in the
4094
4113
  * generated CSS and otherwise paint over `border-danger`).
4095
4114
  *
4115
+ * Use `danger` (`#ff001c`) for both border and focus ring so field chrome
4116
+ * matches validation copy (not the darker accessible `danger-dark` text token).
4117
+ *
4096
4118
  * Keep these literals static for Tailwind content scanning in consumer apps.
4097
4119
  */
4098
- const FORM_FIELD_ERROR_CLASS = '!border-danger dark:!border-danger focus-within:!outline-danger';
4120
+ const FORM_FIELD_ERROR_CLASS = '!border-danger dark:!border-danger focus-within:!ring-danger focus:!ring-danger';
4099
4121
  /** Native input / textarea inside a shell. */
4100
4122
  const FORM_CONTROL_INNER_CLASS = 'flex-1 min-w-0 min-h-0 h-full w-full bg-transparent border-0 outline-none text-body text-ink dark:text-white px-3 py-0 ' +
4101
- 'placeholder:text-neutral-400 disabled:cursor-not-allowed';
4123
+ 'placeholder:text-neutral-500 disabled:cursor-not-allowed';
4102
4124
  /** Prefix / suffix projection gutters. Hidden when nothing is projected. */
4103
4125
  const FORM_AFFIX_CLASS = 'inline-flex items-center shrink-0 px-2.5 text-neutral-600 dark:text-neutral-400 empty:hidden';
4104
4126
  /**
@@ -4113,7 +4135,7 @@ const FORM_DATE_INNER_CLASS = FORM_CONTROL_INNER_CLASS +
4113
4135
  ' cursor-pointer [&::-webkit-calendar-picker-indicator]:cursor-pointer';
4114
4136
  /** Hint under the control (hidden when error is shown). */
4115
4137
  const FORM_HINT_CLASS = 'mt-1.5 text-caption text-neutral-600 dark:text-neutral-400 m-0';
4116
- /** Field-level validation message (not ErrorStateComponent). */
4138
+ /** Field-level validation message (not ErrorStateComponent). Matches `#ff001c`. */
4117
4139
  const FORM_ERROR_CLASS = 'mt-1.5 text-caption text-danger m-0';
4118
4140
  /** Disabled shell — dimmed with not-allowed cursor over the full field chrome. */
4119
4141
  const FORM_DISABLED_CLASS = 'opacity-50 cursor-not-allowed';
@@ -4344,229 +4366,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
4344
4366
  }]
4345
4367
  }], propDecorators: { dateInput: [{ type: i0.ViewChild, args: ['dateInput', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabledInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
4346
4368
 
4347
- let nextRadioId = 0;
4348
- /**
4349
- * Radio group following the shared AFRICANIES form field pattern.
4350
- *
4351
- * **Prefix/suffix slots are omitted** — radios are mutually exclusive options
4352
- * with labels; affix projection does not apply.
4353
- *
4354
- * Selected ring and center disc follow {@link ModeColorService}
4355
- * (SFN green / STN orange).
4356
- *
4357
- * @typeParam T - Option value type.
4358
- *
4359
- * @example
4360
- * ```html
4361
- * <africanies-radio
4362
- * label="Shipment mode"
4363
- * [options]="modeOptions"
4364
- * [(value)]="mode"
4365
- * />
4366
- * ```
4367
- */
4368
- class RadioComponent {
4369
- modeColor = inject(ModeColorService);
4370
- groupName = `africanies-radio-${++nextRadioId}`;
4371
- hintId = `${this.groupName}-hint`;
4372
- errorId = `${this.groupName}-error`;
4373
- labelClass = FORM_LABEL_CLASS;
4374
- hintClass = FORM_HINT_CLASS;
4375
- errorClass = FORM_ERROR_CLASS;
4376
- /** Group legend / label. */
4377
- label = input('', /* @ts-ignore */
4378
- ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
4379
- /** Helper copy. Hidden while {@link error} is set. */
4380
- hint = input(undefined, /* @ts-ignore */
4381
- ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
4382
- /** Field-level validation message. Distinct from `ErrorStateComponent`. */
4383
- error = input(null, /* @ts-ignore */
4384
- ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
4385
- /** Options rendered as radios. */
4386
- options = input([], /* @ts-ignore */
4387
- ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
4388
- /** Selected option value (`[(value)]` + CVA). */
4389
- value = model(null, /* @ts-ignore */
4390
- ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
4391
- /** Host disable flag (in addition to CVA). */
4392
- disabledInput = input(false, { ...(ngDevMode ? { debugName: "disabledInput" } : /* istanbul ignore next */ {}), alias: 'disabled',
4393
- transform: booleanAttribute });
4394
- cvaDisabled = signal(false, /* @ts-ignore */
4395
- ...(ngDevMode ? [{ debugName: "cvaDisabled" }] : /* istanbul ignore next */ []));
4396
- disabled = computed(() => this.disabledInput() || this.cvaDisabled(), /* @ts-ignore */
4397
- ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
4398
- describedBy = computed(() => {
4399
- if (this.error()) {
4400
- return this.errorId;
4401
- }
4402
- if (this.hint()) {
4403
- return this.hintId;
4404
- }
4405
- return null;
4406
- }, /* @ts-ignore */
4407
- ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
4408
- onChange = () => undefined;
4409
- onTouched = () => undefined;
4410
- trackOption(opt) {
4411
- return String(opt.value);
4412
- }
4413
- isSelected(opt) {
4414
- return Object.is(this.value(), opt.value);
4415
- }
4416
- markClass(opt) {
4417
- const base = 'pointer-events-none flex size-4 items-center justify-center rounded-full border-2 bg-transparent transition-colors ' +
4418
- 'peer-focus-visible:outline peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-ink';
4419
- const err = this.error() ? ' !border-danger dark:!border-danger' : '';
4420
- if (this.isSelected(opt)) {
4421
- // Ring uses mode accent; fill stays transparent so the center dot reads clearly.
4422
- return `${base} ${this.modeColor.classes().border}${err}`;
4423
- }
4424
- return `${base} border-neutral-400 dark:border-white${err}`;
4425
- }
4426
- /** Selected inner disc — SFN green / STN orange. */
4427
- dotClass = computed(() => `block size-1.5 rounded-full ${this.modeColor.classes().bg}`, /* @ts-ignore */
4428
- ...(ngDevMode ? [{ debugName: "dotClass" }] : /* istanbul ignore next */ []));
4429
- /** @param value - Selected option value or `null`. */
4430
- writeValue(value) {
4431
- this.value.set(value);
4432
- }
4433
- /** @param fn - Selection change callback. */
4434
- registerOnChange(fn) {
4435
- this.onChange = fn;
4436
- }
4437
- /** @param fn - Blur callback. */
4438
- registerOnTouched(fn) {
4439
- this.onTouched = fn;
4440
- }
4441
- /** @param isDisabled - Blocks interaction when true. */
4442
- setDisabledState(isDisabled) {
4443
- this.cvaDisabled.set(isDisabled);
4444
- }
4445
- select(opt) {
4446
- if (this.disabled() || opt.disabled) {
4447
- return;
4448
- }
4449
- this.value.set(opt.value);
4450
- this.onChange(opt.value);
4451
- }
4452
- onBlur() {
4453
- this.onTouched();
4454
- }
4455
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: RadioComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4456
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: RadioComponent, isStandalone: true, selector: "africanies-radio", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { classAttribute: "inline-flex max-w-full" }, providers: [
4457
- {
4458
- provide: NG_VALUE_ACCESSOR,
4459
- useExisting: forwardRef(() => RadioComponent),
4460
- multi: true,
4461
- },
4462
- ], ngImport: i0, template: `
4463
- <fieldset
4464
- class="m-0 p-0 border-0 min-w-0"
4465
- [attr.aria-invalid]="error() ? true : null"
4466
- [attr.aria-describedby]="describedBy()"
4467
- >
4468
- <legend [class]="labelClass">{{ label() }}</legend>
4469
- <div class="flex flex-row flex-wrap gap-x-4 gap-y-2" role="radiogroup">
4470
- @for (opt of options(); track trackOption(opt)) {
4471
- <label
4472
- class="inline-flex items-center gap-2.5 text-body text-ink dark:text-white"
4473
- [class.opacity-50]="disabled() || opt.disabled"
4474
- [class.cursor-pointer]="!(disabled() || opt.disabled)"
4475
- [class.cursor-not-allowed]="disabled() || opt.disabled"
4476
- >
4477
- <span class="relative inline-flex size-4 shrink-0">
4478
- <input
4479
- type="radio"
4480
- class="peer absolute inset-0 z-10 m-0 size-full opacity-0"
4481
- [class.cursor-pointer]="!(disabled() || opt.disabled)"
4482
- [class.cursor-not-allowed]="disabled() || opt.disabled"
4483
- [name]="groupName"
4484
- [checked]="isSelected(opt)"
4485
- [disabled]="disabled() || !!opt.disabled"
4486
- (change)="select(opt)"
4487
- (blur)="onBlur()"
4488
- />
4489
- <span [class]="markClass(opt)" aria-hidden="true">
4490
- @if (isSelected(opt)) {
4491
- <span [class]="dotClass()"></span>
4492
- }
4493
- </span>
4494
- </span>
4495
- <span>{{ opt.label }}</span>
4496
- </label>
4497
- }
4498
- </div>
4499
- @if (error(); as err) {
4500
- <p [id]="errorId" [class]="errorClass" role="alert">{{ err }}</p>
4501
- } @else if (hint(); as h) {
4502
- <p [id]="hintId" [class]="hintClass">{{ h }}</p>
4503
- }
4504
- </fieldset>
4505
- `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
4506
- }
4507
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: RadioComponent, decorators: [{
4508
- type: Component,
4509
- args: [{
4510
- selector: 'africanies-radio',
4511
- standalone: true,
4512
- changeDetection: ChangeDetectionStrategy.OnPush,
4513
- host: {
4514
- class: 'inline-flex max-w-full',
4515
- },
4516
- providers: [
4517
- {
4518
- provide: NG_VALUE_ACCESSOR,
4519
- useExisting: forwardRef(() => RadioComponent),
4520
- multi: true,
4521
- },
4522
- ],
4523
- template: `
4524
- <fieldset
4525
- class="m-0 p-0 border-0 min-w-0"
4526
- [attr.aria-invalid]="error() ? true : null"
4527
- [attr.aria-describedby]="describedBy()"
4528
- >
4529
- <legend [class]="labelClass">{{ label() }}</legend>
4530
- <div class="flex flex-row flex-wrap gap-x-4 gap-y-2" role="radiogroup">
4531
- @for (opt of options(); track trackOption(opt)) {
4532
- <label
4533
- class="inline-flex items-center gap-2.5 text-body text-ink dark:text-white"
4534
- [class.opacity-50]="disabled() || opt.disabled"
4535
- [class.cursor-pointer]="!(disabled() || opt.disabled)"
4536
- [class.cursor-not-allowed]="disabled() || opt.disabled"
4537
- >
4538
- <span class="relative inline-flex size-4 shrink-0">
4539
- <input
4540
- type="radio"
4541
- class="peer absolute inset-0 z-10 m-0 size-full opacity-0"
4542
- [class.cursor-pointer]="!(disabled() || opt.disabled)"
4543
- [class.cursor-not-allowed]="disabled() || opt.disabled"
4544
- [name]="groupName"
4545
- [checked]="isSelected(opt)"
4546
- [disabled]="disabled() || !!opt.disabled"
4547
- (change)="select(opt)"
4548
- (blur)="onBlur()"
4549
- />
4550
- <span [class]="markClass(opt)" aria-hidden="true">
4551
- @if (isSelected(opt)) {
4552
- <span [class]="dotClass()"></span>
4553
- }
4554
- </span>
4555
- </span>
4556
- <span>{{ opt.label }}</span>
4557
- </label>
4558
- }
4559
- </div>
4560
- @if (error(); as err) {
4561
- <p [id]="errorId" [class]="errorClass" role="alert">{{ err }}</p>
4562
- } @else if (hint(); as h) {
4563
- <p [id]="hintId" [class]="hintClass">{{ h }}</p>
4564
- }
4565
- </fieldset>
4566
- `,
4567
- }]
4568
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabledInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
4569
-
4570
4369
  /**
4571
4370
  * Tailwind panel classes for each {@link ModalSize}.
4572
4371
  *
@@ -4880,12 +4679,12 @@ class ModalService {
4880
4679
  ],
4881
4680
  panelClass: [
4882
4681
  'africanies-modal-panel',
4883
- 'bg-white',
4682
+ 'bg-surface',
4884
4683
  'dark:bg-ink-950',
4885
4684
  'text-ink',
4886
4685
  'dark:text-white',
4887
- 'rounded-lg',
4888
- 'shadow-xl',
4686
+ 'rounded-panel',
4687
+ 'shadow-floating',
4889
4688
  'border',
4890
4689
  'border-border',
4891
4690
  'dark:border-white/15',
@@ -5809,7 +5608,7 @@ class SelectComponent {
5809
5608
  <input
5810
5609
  #searchInput
5811
5610
  type="search"
5812
- class="w-full rounded-md border border-neutral-300 dark:border-white/25 bg-transparent text-body text-ink dark:text-white px-2.5 py-1.5 outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink"
5611
+ class="w-full rounded-md border border-neutral-300 dark:border-white/25 bg-transparent text-body text-ink dark:text-white px-2.5 py-1.5 outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ink"
5813
5612
  [value]="searchQuery()"
5814
5613
  placeholder="Search…"
5815
5614
  (input)="onSearchInput($event)"
@@ -6137,7 +5936,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
6137
5936
  <input
6138
5937
  #searchInput
6139
5938
  type="search"
6140
- class="w-full rounded-md border border-neutral-300 dark:border-white/25 bg-transparent text-body text-ink dark:text-white px-2.5 py-1.5 outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink"
5939
+ class="w-full rounded-md border border-neutral-300 dark:border-white/25 bg-transparent text-body text-ink dark:text-white px-2.5 py-1.5 outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ink"
6141
5940
  [value]="searchQuery()"
6142
5941
  placeholder="Search…"
6143
5942
  (input)="onSearchInput($event)"
@@ -6804,13 +6603,6 @@ class FilterDrawerPanel {
6804
6603
  const opt = Array.isArray(selected) ? selected[0] : selected;
6805
6604
  this.draft.update((s) => ({ ...s, order: opt?.value }));
6806
6605
  }
6807
- onRadioChange(key, value) {
6808
- if (value == null) {
6809
- this.clearField(key);
6810
- return;
6811
- }
6812
- this.setFieldValue(key, value);
6813
- }
6814
6606
  onSelectChange(key, selected) {
6815
6607
  const opt = Array.isArray(selected) ? selected[0] : selected;
6816
6608
  if (!opt) {
@@ -6951,11 +6743,18 @@ class FilterDrawerPanel {
6951
6743
  value,
6952
6744
  });
6953
6745
  }
6954
- toRadioOptions(options) {
6955
- return (options ?? []).map((o) => ({
6956
- label: o.label,
6957
- value: o.value,
6958
- }));
6746
+ /**
6747
+ * Boolean Yes/No chips. Prefer field.options when present; otherwise default
6748
+ * colored Yes/No so every boolean filter stays visually distinct.
6749
+ */
6750
+ booleanOptions(field) {
6751
+ if (field.options?.length) {
6752
+ return field.options;
6753
+ }
6754
+ return [
6755
+ { value: '1', label: 'Yes', color: '#25945c' },
6756
+ { value: '0', label: 'No', color: '#FF001C' },
6757
+ ];
6959
6758
  }
6960
6759
  onReset() {
6961
6760
  const next = resetFilterState(true, this.draft());
@@ -7204,12 +7003,37 @@ class FilterDrawerPanel {
7204
7003
  </div>
7205
7004
  }
7206
7005
  @case ('boolean') {
7207
- <africanies-radio
7208
- label=""
7209
- [options]="toRadioOptions(field.options)"
7210
- [value]="draft().values[field.key] ?? null"
7211
- (valueChange)="onRadioChange(field.key, $event)"
7212
- />
7006
+ <div class="flex flex-wrap gap-2">
7007
+ @for (opt of booleanOptions(field); track opt.value) {
7008
+ <button
7009
+ type="button"
7010
+ class="cursor-pointer rounded-md border px-2.5 py-1.5 text-body-sm transition-colors"
7011
+ [style.color]="
7012
+ draft().values[field.key] === opt.value
7013
+ ? opt.color ?? null
7014
+ : null
7015
+ "
7016
+ [style.border-color]="
7017
+ draft().values[field.key] === opt.value
7018
+ ? opt.color ?? null
7019
+ : null
7020
+ "
7021
+ [style.background-color]="
7022
+ draft().values[field.key] === opt.value && opt.color
7023
+ ? opt.color + '1A'
7024
+ : null
7025
+ "
7026
+ [class]="
7027
+ draft().values[field.key] === opt.value
7028
+ ? 'font-medium'
7029
+ : 'border-neutral-300 text-neutral-600 hover:border-neutral-400 dark:border-white/15 dark:text-neutral-300'
7030
+ "
7031
+ (click)="setFieldValue(field.key, opt.value)"
7032
+ >
7033
+ {{ opt.label }}
7034
+ </button>
7035
+ }
7036
+ </div>
7213
7037
  }
7214
7038
  @case ('select') {
7215
7039
  <!-- Label already shown in the section header — avoid double label. -->
@@ -7268,7 +7092,7 @@ class FilterDrawerPanel {
7268
7092
  type="button"
7269
7093
  variant="ghost"
7270
7094
  size="sm"
7271
- class="shrink-0 !min-h-0 self-start !px-1 !py-0 text-neutral-500 hover:text-ink dark:text-neutral-400 dark:hover:text-white"
7095
+ class="shrink-0 !min-h-0 self-start !px-1 !py-0 text-neutral-600 hover:text-ink dark:text-neutral-400 dark:hover:text-white"
7272
7096
  aria-label="Dismiss"
7273
7097
  (click)="applyError.set(null)"
7274
7098
  >
@@ -7300,7 +7124,7 @@ class FilterDrawerPanel {
7300
7124
  </div>
7301
7125
  </div>
7302
7126
  </div>
7303
- `, isInline: true, dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "component", type: ButtonComponent, selector: "button[africanies-button], a[africanies-button]", inputs: ["variant", "size", "disabled", "loading"] }, { kind: "component", type: DatePickerComponent, selector: "africanies-date-picker", inputs: ["label", "hint", "error", "min", "max", "value", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: RadioComponent, selector: "africanies-radio", inputs: ["label", "hint", "error", "options", "value", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: SelectComponent, selector: "africanies-select", inputs: ["label", "hint", "error", "showRetry", "retryLabel", "retryingLabel", "retrying", "loading", "loadingLabel", "placeholder", "options", "selected", "searchable", "allowFreeText", "multiple", "maxSelected", "create", "disabled", "showTriggerIcon", "size"], outputs: ["retry", "searchChange", "optionsChange", "selectedChange"] }, { kind: "component", type: TextInputComponent, selector: "africanies-text-input", inputs: ["label", "hint", "error", "placeholder", "type", "autocomplete", "value", "disabled"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
7127
+ `, isInline: true, dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "component", type: ButtonComponent, selector: "button[africanies-button], a[africanies-button]", inputs: ["variant", "size", "disabled", "loading"] }, { kind: "component", type: DatePickerComponent, selector: "africanies-date-picker", inputs: ["label", "hint", "error", "min", "max", "value", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: SelectComponent, selector: "africanies-select", inputs: ["label", "hint", "error", "showRetry", "retryLabel", "retryingLabel", "retrying", "loading", "loadingLabel", "placeholder", "options", "selected", "searchable", "allowFreeText", "multiple", "maxSelected", "create", "disabled", "showTriggerIcon", "size"], outputs: ["retry", "searchChange", "optionsChange", "selectedChange"] }, { kind: "component", type: TextInputComponent, selector: "africanies-text-input", inputs: ["label", "hint", "error", "placeholder", "type", "autocomplete", "value", "disabled"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
7304
7128
  }
7305
7129
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FilterDrawerPanel, decorators: [{
7306
7130
  type: Component,
@@ -7315,7 +7139,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
7315
7139
  AfricaniesIconComponent,
7316
7140
  ButtonComponent,
7317
7141
  DatePickerComponent,
7318
- RadioComponent,
7319
7142
  SelectComponent,
7320
7143
  TextInputComponent,
7321
7144
  ],
@@ -7441,7 +7264,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
7441
7264
  @switch (field.type) {
7442
7265
  @case ('enum') {
7443
7266
  <div class="flex flex-wrap gap-2">
7444
- @for (opt of field.options ?? []; track opt.value) {
7267
+ @for (opt of field.options ?? []; track opt.value) {
7268
+ <button
7269
+ type="button"
7270
+ class="cursor-pointer rounded-md border px-2.5 py-1.5 text-body-sm transition-colors"
7271
+ [style.color]="
7272
+ draft().values[field.key] === opt.value
7273
+ ? opt.color ?? null
7274
+ : null
7275
+ "
7276
+ [style.border-color]="
7277
+ draft().values[field.key] === opt.value
7278
+ ? opt.color ?? null
7279
+ : null
7280
+ "
7281
+ [style.background-color]="
7282
+ draft().values[field.key] === opt.value && opt.color
7283
+ ? opt.color + '1A'
7284
+ : null
7285
+ "
7286
+ [class]="
7287
+ draft().values[field.key] === opt.value
7288
+ ? 'font-medium'
7289
+ : 'border-neutral-300 text-neutral-600 hover:border-neutral-400 dark:border-white/15 dark:text-neutral-300'
7290
+ "
7291
+ (click)="setFieldValue(field.key, opt.value)"
7292
+ >
7293
+ {{ opt.label }}
7294
+ </button>
7295
+ }
7296
+ </div>
7297
+ }
7298
+ @case ('boolean') {
7299
+ <div class="flex flex-wrap gap-2">
7300
+ @for (opt of booleanOptions(field); track opt.value) {
7445
7301
  <button
7446
7302
  type="button"
7447
7303
  class="cursor-pointer rounded-md border px-2.5 py-1.5 text-body-sm transition-colors"
@@ -7472,14 +7328,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
7472
7328
  }
7473
7329
  </div>
7474
7330
  }
7475
- @case ('boolean') {
7476
- <africanies-radio
7477
- label=""
7478
- [options]="toRadioOptions(field.options)"
7479
- [value]="draft().values[field.key] ?? null"
7480
- (valueChange)="onRadioChange(field.key, $event)"
7481
- />
7482
- }
7483
7331
  @case ('select') {
7484
7332
  <!-- Label already shown in the section header — avoid double label. -->
7485
7333
  <africanies-select
@@ -7537,7 +7385,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
7537
7385
  type="button"
7538
7386
  variant="ghost"
7539
7387
  size="sm"
7540
- class="shrink-0 !min-h-0 self-start !px-1 !py-0 text-neutral-500 hover:text-ink dark:text-neutral-400 dark:hover:text-white"
7388
+ class="shrink-0 !min-h-0 self-start !px-1 !py-0 text-neutral-600 hover:text-ink dark:text-neutral-400 dark:hover:text-white"
7541
7389
  aria-label="Dismiss"
7542
7390
  (click)="applyError.set(null)"
7543
7391
  >
@@ -7617,11 +7465,11 @@ class DrawerService {
7617
7465
  ],
7618
7466
  panelClass: [
7619
7467
  'africanies-drawer-panel',
7620
- 'bg-white',
7468
+ 'bg-surface',
7621
7469
  'dark:bg-ink-950',
7622
7470
  'text-ink',
7623
7471
  'dark:text-white',
7624
- 'shadow-xl',
7472
+ 'shadow-floating',
7625
7473
  'border-l',
7626
7474
  'border-border',
7627
7475
  'dark:border-white/15',
@@ -7754,8 +7602,8 @@ class ConfirmDialogComponent {
7754
7602
  iconWellClass = computed(() => {
7755
7603
  const well = 'inline-flex size-14 shrink-0 items-center justify-center rounded-2xl';
7756
7604
  return this.options.danger
7757
- ? `${well} bg-danger-subtle text-danger dark:bg-danger/20 dark:text-danger`
7758
- : `${well} bg-export-subtle text-export dark:bg-export/20 dark:text-export-light`;
7605
+ ? `${well} bg-danger-subtle text-danger-dark dark:bg-danger/20 dark:text-danger`
7606
+ : `${well} bg-export-subtle text-export-strong dark:bg-export/20 dark:text-export-light`;
7759
7607
  }, /* @ts-ignore */
7760
7608
  ...(ngDevMode ? [{ debugName: "iconWellClass" }] : /* istanbul ignore next */ []));
7761
7609
  constructor() {
@@ -8141,7 +7989,7 @@ class OverlayFrameComponent {
8141
7989
  <ng-content select="[africaniesOverlayHeader]" />
8142
7990
  </div>
8143
7991
  <div
8144
- class="africanies-overlay-scroll min-h-0 flex-1 overflow-x-clip overflow-y-auto px-[2px] -mx-[2px]"
7992
+ class="africanies-overlay-scroll min-h-0 flex-1 overflow-x-clip overflow-y-auto px-1 -mx-1"
8145
7993
  >
8146
7994
  <ng-content />
8147
7995
  </div>
@@ -8164,7 +8012,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
8164
8012
  <ng-content select="[africaniesOverlayHeader]" />
8165
8013
  </div>
8166
8014
  <div
8167
- class="africanies-overlay-scroll min-h-0 flex-1 overflow-x-clip overflow-y-auto px-[2px] -mx-[2px]"
8015
+ class="africanies-overlay-scroll min-h-0 flex-1 overflow-x-clip overflow-y-auto px-1 -mx-1"
8168
8016
  >
8169
8017
  <ng-content />
8170
8018
  </div>
@@ -9056,7 +8904,7 @@ class CheckboxComponent {
9056
8904
  ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
9057
8905
  markClass = computed(() => {
9058
8906
  const base = 'pointer-events-none flex size-4 items-center justify-center rounded border text-white transition-colors ' +
9059
- 'peer-focus-visible:outline peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-ink';
8907
+ FORM_FOCUS_VISIBLE_PEER_CLASS;
9060
8908
  const err = this.error() ? ' !border-danger dark:!border-danger' : '';
9061
8909
  if (this.value()) {
9062
8910
  const c = this.modeColor.classes();
@@ -9731,7 +9579,7 @@ class FilePreviewDialogComponent {
9731
9579
  <div
9732
9580
  class="flex flex-col items-center gap-3 rounded-md bg-background-welcome px-6 py-10 text-center ring-1 ring-border dark:bg-white/5 dark:ring-white/15"
9733
9581
  >
9734
- <africanies-icon name="eye-slash" [size]="32" class="text-neutral-500" />
9582
+ <africanies-icon name="eye-slash" [size]="32" class="text-neutral-600" />
9735
9583
  <p class="m-0 text-body font-medium text-ink dark:text-white">
9736
9584
  Preview not available
9737
9585
  </p>
@@ -9801,7 +9649,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
9801
9649
  <div
9802
9650
  class="flex flex-col items-center gap-3 rounded-md bg-background-welcome px-6 py-10 text-center ring-1 ring-border dark:bg-white/5 dark:ring-white/15"
9803
9651
  >
9804
- <africanies-icon name="eye-slash" [size]="32" class="text-neutral-500" />
9652
+ <africanies-icon name="eye-slash" [size]="32" class="text-neutral-600" />
9805
9653
  <p class="m-0 text-body font-medium text-ink dark:text-white">
9806
9654
  Preview not available
9807
9655
  </p>
@@ -10494,7 +10342,7 @@ class FileUploadComponent {
10494
10342
  <span> · </span>
10495
10343
  <button
10496
10344
  type="button"
10497
- class="cursor-pointer bg-transparent p-0 text-caption font-medium text-ink underline underline-offset-2 hover:text-neutral-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-white dark:hover:text-neutral-200"
10345
+ class="cursor-pointer bg-transparent p-0 text-caption font-medium text-ink underline underline-offset-2 hover:text-neutral-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ink dark:text-white dark:hover:text-neutral-200"
10498
10346
  (click)="openPreview(item); $event.stopPropagation()"
10499
10347
  >
10500
10348
  View
@@ -10794,7 +10642,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
10794
10642
  <span> · </span>
10795
10643
  <button
10796
10644
  type="button"
10797
- class="cursor-pointer bg-transparent p-0 text-caption font-medium text-ink underline underline-offset-2 hover:text-neutral-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-white dark:hover:text-neutral-200"
10645
+ class="cursor-pointer bg-transparent p-0 text-caption font-medium text-ink underline underline-offset-2 hover:text-neutral-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ink dark:text-white dark:hover:text-neutral-200"
10798
10646
  (click)="openPreview(item); $event.stopPropagation()"
10799
10647
  >
10800
10648
  View
@@ -11208,7 +11056,7 @@ class OtpInputComponent {
11208
11056
  cellClass = computed(() => {
11209
11057
  let classes = 'size-10 shrink-0 rounded-md border border-neutral-300 bg-white text-center text-body font-medium text-ink outline-none transition-colors ' +
11210
11058
  'dark:border-white/25 dark:bg-ink-950 dark:text-white ' +
11211
- 'focus:outline focus:outline-2 focus:outline-offset-2 focus:outline-ink disabled:cursor-not-allowed';
11059
+ `${FORM_FOCUS_CLASS} disabled:cursor-not-allowed`;
11212
11060
  if (this.masked()) {
11213
11061
  classes += ' africanies-otp-cell-masked text-body-lg';
11214
11062
  }
@@ -11617,6 +11465,229 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
11617
11465
  `, styles: [".africanies-otp-cell-masked{-webkit-text-security:disc;text-security:disc}\n"] }]
11618
11466
  }], ctorParameters: () => [], propDecorators: { cellInputs: [{ type: i0.ViewChildren, args: ['cellInput', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], length: [{ type: i0.Input, args: [{ isSignal: true, alias: "length", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabledInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showResend: [{ type: i0.Input, args: [{ isSignal: true, alias: "showResend", required: false }] }], resendPrompt: [{ type: i0.Input, args: [{ isSignal: true, alias: "resendPrompt", required: false }] }], resendLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "resendLabel", required: false }] }], resendCooldown: [{ type: i0.Input, args: [{ isSignal: true, alias: "resendCooldown", required: false }] }], completed: [{ type: i0.Output, args: ["completed"] }], resend: [{ type: i0.Output, args: ["resend"] }] } });
11619
11467
 
11468
+ let nextRadioId = 0;
11469
+ /**
11470
+ * Radio group following the shared AFRICANIES form field pattern.
11471
+ *
11472
+ * **Prefix/suffix slots are omitted** — radios are mutually exclusive options
11473
+ * with labels; affix projection does not apply.
11474
+ *
11475
+ * Selected ring and center disc follow {@link ModeColorService}
11476
+ * (SFN green / STN orange).
11477
+ *
11478
+ * @typeParam T - Option value type.
11479
+ *
11480
+ * @example
11481
+ * ```html
11482
+ * <africanies-radio
11483
+ * label="Shipment mode"
11484
+ * [options]="modeOptions"
11485
+ * [(value)]="mode"
11486
+ * />
11487
+ * ```
11488
+ */
11489
+ class RadioComponent {
11490
+ modeColor = inject(ModeColorService);
11491
+ groupName = `africanies-radio-${++nextRadioId}`;
11492
+ hintId = `${this.groupName}-hint`;
11493
+ errorId = `${this.groupName}-error`;
11494
+ labelClass = FORM_LABEL_CLASS;
11495
+ hintClass = FORM_HINT_CLASS;
11496
+ errorClass = FORM_ERROR_CLASS;
11497
+ /** Group legend / label. */
11498
+ label = input('', /* @ts-ignore */
11499
+ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
11500
+ /** Helper copy. Hidden while {@link error} is set. */
11501
+ hint = input(undefined, /* @ts-ignore */
11502
+ ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
11503
+ /** Field-level validation message. Distinct from `ErrorStateComponent`. */
11504
+ error = input(null, /* @ts-ignore */
11505
+ ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
11506
+ /** Options rendered as radios. */
11507
+ options = input([], /* @ts-ignore */
11508
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
11509
+ /** Selected option value (`[(value)]` + CVA). */
11510
+ value = model(null, /* @ts-ignore */
11511
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
11512
+ /** Host disable flag (in addition to CVA). */
11513
+ disabledInput = input(false, { ...(ngDevMode ? { debugName: "disabledInput" } : /* istanbul ignore next */ {}), alias: 'disabled',
11514
+ transform: booleanAttribute });
11515
+ cvaDisabled = signal(false, /* @ts-ignore */
11516
+ ...(ngDevMode ? [{ debugName: "cvaDisabled" }] : /* istanbul ignore next */ []));
11517
+ disabled = computed(() => this.disabledInput() || this.cvaDisabled(), /* @ts-ignore */
11518
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
11519
+ describedBy = computed(() => {
11520
+ if (this.error()) {
11521
+ return this.errorId;
11522
+ }
11523
+ if (this.hint()) {
11524
+ return this.hintId;
11525
+ }
11526
+ return null;
11527
+ }, /* @ts-ignore */
11528
+ ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
11529
+ onChange = () => undefined;
11530
+ onTouched = () => undefined;
11531
+ trackOption(opt) {
11532
+ return String(opt.value);
11533
+ }
11534
+ isSelected(opt) {
11535
+ return Object.is(this.value(), opt.value);
11536
+ }
11537
+ markClass(opt) {
11538
+ const base = 'pointer-events-none flex size-4 items-center justify-center rounded-full border-2 bg-transparent transition-colors ' +
11539
+ FORM_FOCUS_VISIBLE_PEER_CLASS;
11540
+ const err = this.error() ? ' !border-danger dark:!border-danger' : '';
11541
+ if (this.isSelected(opt)) {
11542
+ // Ring uses mode accent; fill stays transparent so the center dot reads clearly.
11543
+ return `${base} ${this.modeColor.classes().border}${err}`;
11544
+ }
11545
+ return `${base} border-neutral-400 dark:border-white${err}`;
11546
+ }
11547
+ /** Selected inner disc — SFN green / STN orange. */
11548
+ dotClass = computed(() => `block size-1.5 rounded-full ${this.modeColor.classes().bg}`, /* @ts-ignore */
11549
+ ...(ngDevMode ? [{ debugName: "dotClass" }] : /* istanbul ignore next */ []));
11550
+ /** @param value - Selected option value or `null`. */
11551
+ writeValue(value) {
11552
+ this.value.set(value);
11553
+ }
11554
+ /** @param fn - Selection change callback. */
11555
+ registerOnChange(fn) {
11556
+ this.onChange = fn;
11557
+ }
11558
+ /** @param fn - Blur callback. */
11559
+ registerOnTouched(fn) {
11560
+ this.onTouched = fn;
11561
+ }
11562
+ /** @param isDisabled - Blocks interaction when true. */
11563
+ setDisabledState(isDisabled) {
11564
+ this.cvaDisabled.set(isDisabled);
11565
+ }
11566
+ select(opt) {
11567
+ if (this.disabled() || opt.disabled) {
11568
+ return;
11569
+ }
11570
+ this.value.set(opt.value);
11571
+ this.onChange(opt.value);
11572
+ }
11573
+ onBlur() {
11574
+ this.onTouched();
11575
+ }
11576
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: RadioComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11577
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: RadioComponent, isStandalone: true, selector: "africanies-radio", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { classAttribute: "inline-flex max-w-full" }, providers: [
11578
+ {
11579
+ provide: NG_VALUE_ACCESSOR,
11580
+ useExisting: forwardRef(() => RadioComponent),
11581
+ multi: true,
11582
+ },
11583
+ ], ngImport: i0, template: `
11584
+ <fieldset
11585
+ class="m-0 p-0 border-0 min-w-0"
11586
+ [attr.aria-invalid]="error() ? true : null"
11587
+ [attr.aria-describedby]="describedBy()"
11588
+ >
11589
+ <legend [class]="labelClass">{{ label() }}</legend>
11590
+ <div class="flex flex-row flex-wrap gap-x-4 gap-y-2" role="radiogroup">
11591
+ @for (opt of options(); track trackOption(opt)) {
11592
+ <label
11593
+ class="inline-flex items-center gap-2.5 text-body text-ink dark:text-white"
11594
+ [class.opacity-50]="disabled() || opt.disabled"
11595
+ [class.cursor-pointer]="!(disabled() || opt.disabled)"
11596
+ [class.cursor-not-allowed]="disabled() || opt.disabled"
11597
+ >
11598
+ <span class="relative inline-flex size-4 shrink-0">
11599
+ <input
11600
+ type="radio"
11601
+ class="peer absolute inset-0 z-10 m-0 size-full opacity-0"
11602
+ [class.cursor-pointer]="!(disabled() || opt.disabled)"
11603
+ [class.cursor-not-allowed]="disabled() || opt.disabled"
11604
+ [name]="groupName"
11605
+ [checked]="isSelected(opt)"
11606
+ [disabled]="disabled() || !!opt.disabled"
11607
+ (change)="select(opt)"
11608
+ (blur)="onBlur()"
11609
+ />
11610
+ <span [class]="markClass(opt)" aria-hidden="true">
11611
+ @if (isSelected(opt)) {
11612
+ <span [class]="dotClass()"></span>
11613
+ }
11614
+ </span>
11615
+ </span>
11616
+ <span>{{ opt.label }}</span>
11617
+ </label>
11618
+ }
11619
+ </div>
11620
+ @if (error(); as err) {
11621
+ <p [id]="errorId" [class]="errorClass" role="alert">{{ err }}</p>
11622
+ } @else if (hint(); as h) {
11623
+ <p [id]="hintId" [class]="hintClass">{{ h }}</p>
11624
+ }
11625
+ </fieldset>
11626
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
11627
+ }
11628
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: RadioComponent, decorators: [{
11629
+ type: Component,
11630
+ args: [{
11631
+ selector: 'africanies-radio',
11632
+ standalone: true,
11633
+ changeDetection: ChangeDetectionStrategy.OnPush,
11634
+ host: {
11635
+ class: 'inline-flex max-w-full',
11636
+ },
11637
+ providers: [
11638
+ {
11639
+ provide: NG_VALUE_ACCESSOR,
11640
+ useExisting: forwardRef(() => RadioComponent),
11641
+ multi: true,
11642
+ },
11643
+ ],
11644
+ template: `
11645
+ <fieldset
11646
+ class="m-0 p-0 border-0 min-w-0"
11647
+ [attr.aria-invalid]="error() ? true : null"
11648
+ [attr.aria-describedby]="describedBy()"
11649
+ >
11650
+ <legend [class]="labelClass">{{ label() }}</legend>
11651
+ <div class="flex flex-row flex-wrap gap-x-4 gap-y-2" role="radiogroup">
11652
+ @for (opt of options(); track trackOption(opt)) {
11653
+ <label
11654
+ class="inline-flex items-center gap-2.5 text-body text-ink dark:text-white"
11655
+ [class.opacity-50]="disabled() || opt.disabled"
11656
+ [class.cursor-pointer]="!(disabled() || opt.disabled)"
11657
+ [class.cursor-not-allowed]="disabled() || opt.disabled"
11658
+ >
11659
+ <span class="relative inline-flex size-4 shrink-0">
11660
+ <input
11661
+ type="radio"
11662
+ class="peer absolute inset-0 z-10 m-0 size-full opacity-0"
11663
+ [class.cursor-pointer]="!(disabled() || opt.disabled)"
11664
+ [class.cursor-not-allowed]="disabled() || opt.disabled"
11665
+ [name]="groupName"
11666
+ [checked]="isSelected(opt)"
11667
+ [disabled]="disabled() || !!opt.disabled"
11668
+ (change)="select(opt)"
11669
+ (blur)="onBlur()"
11670
+ />
11671
+ <span [class]="markClass(opt)" aria-hidden="true">
11672
+ @if (isSelected(opt)) {
11673
+ <span [class]="dotClass()"></span>
11674
+ }
11675
+ </span>
11676
+ </span>
11677
+ <span>{{ opt.label }}</span>
11678
+ </label>
11679
+ }
11680
+ </div>
11681
+ @if (error(); as err) {
11682
+ <p [id]="errorId" [class]="errorClass" role="alert">{{ err }}</p>
11683
+ } @else if (hint(); as h) {
11684
+ <p [id]="hintId" [class]="hintClass">{{ h }}</p>
11685
+ }
11686
+ </fieldset>
11687
+ `,
11688
+ }]
11689
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabledInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
11690
+
11620
11691
  /**
11621
11692
  * Radio form control exports.
11622
11693
  */
@@ -12603,7 +12674,7 @@ class ToggleComponent {
12603
12674
  [id]="controlId"
12604
12675
  type="button"
12605
12676
  role="switch"
12606
- class="relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border border-border p-0.5 box-border transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:border-white/15"
12677
+ class="relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border border-border p-0.5 box-border transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ink dark:border-white/15"
12607
12678
  [ngClass]="trackClass()"
12608
12679
  [attr.aria-checked]="on()"
12609
12680
  [attr.aria-busy]="loading() ? true : null"
@@ -12665,7 +12736,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
12665
12736
  [id]="controlId"
12666
12737
  type="button"
12667
12738
  role="switch"
12668
- class="relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border border-border p-0.5 box-border transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:border-white/15"
12739
+ class="relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border border-border p-0.5 box-border transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ink dark:border-white/15"
12669
12740
  [ngClass]="trackClass()"
12670
12741
  [attr.aria-checked]="on()"
12671
12742
  [attr.aria-busy]="loading() ? true : null"
@@ -13476,8 +13547,8 @@ class ShippingModeSwitchComponent {
13476
13547
  mode = this.shipping.mode;
13477
13548
  /** Host padding / divider — tighter when the rail is collapsed. */
13478
13549
  hostClass = computed(() => this.collapsed()
13479
- ? 'block shrink-0 border-t border-strong px-1.5 py-3 dark:border-white/10'
13480
- : 'block shrink-0 border-t border-strong px-2 py-3 dark:border-white/10', /* @ts-ignore */
13550
+ ? 'block shrink-0 border-t border-border px-1.5 py-3 dark:border-white/10'
13551
+ : 'block shrink-0 border-t border-border px-2 py-3 dark:border-white/10', /* @ts-ignore */
13481
13552
  ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
13482
13553
  /**
13483
13554
  * Requests a mode change through {@link ShippingModeService.requestModeChange}
@@ -13501,16 +13572,16 @@ class ShippingModeSwitchComponent {
13501
13572
  const selected = this.mode() === option;
13502
13573
  const compact = this.collapsed();
13503
13574
  const base = 'flex w-full cursor-pointer flex-col items-center justify-center gap-1 rounded-lg border text-center text-caption font-medium transition-colors ' +
13504
- 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink ' +
13575
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus ' +
13505
13576
  (compact ? 'min-h-9 px-1 py-1.5' : 'min-h-[4.5rem] px-1.5 py-2');
13506
- const idle = 'border-border bg-white text-ink hover:bg-background-welcome dark:border-white/15 dark:bg-ink-950 dark:text-white dark:hover:bg-white/10';
13577
+ const idle = 'border-control bg-surface text-ink hover:bg-background-hover dark:border-white/15 dark:bg-ink-950 dark:text-white dark:hover:bg-white/10';
13507
13578
  if (option === 'stn') {
13508
13579
  return selected
13509
- ? `${base} border-import bg-import text-white`
13580
+ ? `${base} border-import-strong bg-import-strong text-white dark:border-import dark:bg-import`
13510
13581
  : `${base} ${idle}`;
13511
13582
  }
13512
13583
  return selected
13513
- ? `${base} border-export bg-export text-white`
13584
+ ? `${base} border-export-strong bg-export-strong text-white dark:border-export dark:bg-export`
13514
13585
  : `${base} ${idle}`;
13515
13586
  }
13516
13587
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ShippingModeSwitchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
@@ -13797,7 +13868,8 @@ const DEFAULT_LINK_ACTIVE$1 = {
13797
13868
  * ## Parent items with children
13798
13869
  * **Expanded rail:** branches start open by default (`expandBranchesByDefault`).
13799
13870
  * Clicking a parent only opens/closes the branch (no route change). When any
13800
- * branch is open, a collapse-all control sits above the list (top right).
13871
+ * branch is open, a sticky collapse-all control sits above the list (top right).
13872
+ * The rail expand/collapse control stays pinned in the logo header.
13801
13873
  * **Collapsed rail:** clicking a parent expands the rail, opens the
13802
13874
  * branch, and activates the first enabled child (router or `activeId`).
13803
13875
  *
@@ -13910,6 +13982,10 @@ class SideNavComponent {
13910
13982
  collapseAllBranches() {
13911
13983
  this.openBranches.set(new Set());
13912
13984
  }
13985
+ /** Open every parent branch (e.g. so the collapse-all control is visible). */
13986
+ expandAllBranches() {
13987
+ this.openBranches.set(new Set(this.collectParentIds(this.items())));
13988
+ }
13913
13989
  onItemEnter(id) {
13914
13990
  if (this.collapsed()) {
13915
13991
  this.hoverId.set(id);
@@ -13942,7 +14018,7 @@ class SideNavComponent {
13942
14018
  }
13943
14019
  rowClass(item) {
13944
14020
  const base = 'group relative flex w-full items-center gap-2 rounded-lg text-body-sm no-underline transition-colors ' +
13945
- 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink ' +
14021
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus ' +
13946
14022
  (this.collapsed() ? 'justify-center px-1.5 py-2' : 'px-3 py-2') +
13947
14023
  (item.disabled ? ' cursor-not-allowed opacity-50' : ' cursor-pointer');
13948
14024
  const leaf = this.isLeafActive(item);
@@ -13956,17 +14032,17 @@ class SideNavComponent {
13956
14032
  if (ancestor) {
13957
14033
  return `${base} bg-transparent ${colors.text} font-semibold`;
13958
14034
  }
13959
- return (`${base} font-medium text-neutral-600 hover:bg-background-welcome hover:text-ink ` +
14035
+ return (`${base} font-medium text-neutral-600 hover:bg-background-hover hover:text-ink ` +
13960
14036
  'dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white');
13961
14037
  }
13962
14038
  childRowClass(item) {
13963
14039
  const base = 'relative flex w-full items-center gap-2 rounded-lg px-3 py-1.5 text-body-sm no-underline transition-colors ' +
13964
- 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink ' +
14040
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus ' +
13965
14041
  (item.disabled ? 'cursor-not-allowed opacity-50 ' : 'cursor-pointer ');
13966
14042
  if (this.isLeafActive(item)) {
13967
14043
  return `${base} ${this.modeColor.classes().activeFill} font-semibold`;
13968
14044
  }
13969
- return (`${base} font-medium text-neutral-600 hover:bg-background-welcome hover:text-ink ` +
14045
+ return (`${base} font-medium text-neutral-600 hover:bg-background-hover hover:text-ink ` +
13970
14046
  'dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white');
13971
14047
  }
13972
14048
  /**
@@ -14109,13 +14185,13 @@ class SideNavComponent {
14109
14185
  return null;
14110
14186
  }
14111
14187
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SideNavComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14112
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: SideNavComponent, isStandalone: true, selector: "africanies-side-nav", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, activeId: { classPropertyName: "activeId", publicName: "activeId", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, expandParents: { classPropertyName: "expandParents", publicName: "expandParents", isSignal: true, isRequired: false, transformFunction: null }, expandBranchesByDefault: { classPropertyName: "expandBranchesByDefault", publicName: "expandBranchesByDefault", isSignal: true, isRequired: false, transformFunction: null }, linkActiveOptions: { classPropertyName: "linkActiveOptions", publicName: "linkActiveOptions", isSignal: true, isRequired: false, transformFunction: null }, showLogo: { classPropertyName: "showLogo", publicName: "showLogo", isSignal: true, isRequired: false, transformFunction: null }, logoSrc: { classPropertyName: "logoSrc", publicName: "logoSrc", isSignal: true, isRequired: false, transformFunction: null }, logoMiniSrc: { classPropertyName: "logoMiniSrc", publicName: "logoMiniSrc", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsed: "collapsedChange", activeId: "activeIdChange" }, host: { classAttribute: "relative z-20 block h-full" }, ngImport: i0, template: " <aside\n class=\"relative flex h-full flex-col overflow-visible border-r border-border bg-white transition-[width] duration-200 ease-out dark:border-white/10 dark:bg-ink-950\"\n [style.width]=\"collapsed() ? '3.75rem' : '15rem'\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n <div\n class=\"pointer-events-none absolute inset-y-0 left-0 w-[3px] transition-colors\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n\n <div\n class=\"flex shrink-0 items-center gap-2 border-b border-border py-3 dark:border-white/10\"\n [class.flex-col]=\"collapsed()\"\n [class.px-3]=\"collapsed()\"\n [class.pl-4]=\"!collapsed()\"\n [class.pr-2]=\"!collapsed()\"\n >\n @if (collapsed()) {\n @if (showLogo()) {\n <img\n [src]=\"resolvedMiniLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"size-8 shrink-0 object-contain\"\n />\n }\n } @else {\n @if (showLogo()) {\n <div class=\"min-w-0 flex-1\">\n <img\n [src]=\"resolvedFullLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"h-9 max-w-full object-contain object-left\"\n />\n </div>\n } @else {\n <div\n class=\"min-w-0 flex-1 truncate text-body-sm font-semibold text-ink dark:text-white\"\n >\n <ng-content select=\"[brand]\" />\n </div>\n }\n }\n <button\n type=\"button\"\n class=\"inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white\"\n [attr.aria-label]=\"\n collapsed() ? 'Expand navigation' : 'Collapse navigation'\n \"\n [attr.aria-expanded]=\"!collapsed()\"\n (click)=\"toggleCollapsed()\"\n >\n <africanies-icon\n [name]=\"collapsed() ? 'angle-double-right' : 'angle-double-left'\"\n [size]=\"16\"\n />\n </button>\n </div>\n\n <nav class=\"africanies-overlay-scroll relative flex-1 overflow-y-auto overflow-x-visible px-3 py-3\">\n @if (!collapsed() && expandParents() && hasOpenBranches()) {\n <button\n type=\"button\"\n class=\"absolute right-1.5 top-1.5 z-10 inline-flex size-7 cursor-pointer items-center justify-center rounded-md bg-white text-neutral-500 shadow-sm ring-1 ring-border/80 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:bg-ink-950 dark:text-neutral-400 dark:ring-white/15 dark:hover:bg-white/10 dark:hover:text-white\"\n aria-label=\"Collapse all\"\n (click)=\"collapseAllBranches()\"\n >\n <africanies-icon name=\"compress\" [size]=\"14\" />\n </button>\n }\n <ul class=\"m-0 flex list-none flex-col gap-0.5 p-0\">\n @for (item of items(); track item.id) {\n <li\n class=\"relative overflow-visible\"\n (mouseenter)=\"onItemEnter(item.id)\"\n (mouseleave)=\"onItemLeave(item.id)\"\n >\n @if (item.routerLink !== null && item.routerLink !== undefined) {\n <a\n [routerLink]=\"item.disabled ? null : item.routerLink\"\n [queryParams]=\"item.queryParams\"\n [fragment]=\"item.fragment\"\n [class]=\"rowClass(item)\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n [attr.aria-disabled]=\"item.disabled ? true : null\"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"rowClass(item)\"\n [disabled]=\"!!item.disabled\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </button>\n }\n\n @if (!collapsed() && item.children?.length) {\n <div\n class=\"grid transition-[grid-template-rows] duration-200 ease-out\"\n [style.grid-template-rows]=\"\n isBranchOpen(item.id) ? '1fr' : '0fr'\n \"\n [attr.aria-hidden]=\"!isBranchOpen(item.id)\"\n [class.pointer-events-none]=\"!isBranchOpen(item.id)\"\n >\n <ul\n class=\"relative m-0 mt-0.5 min-h-0 list-none space-y-0.5 overflow-hidden py-1 pl-4 before:absolute before:bottom-1 before:left-[1.35rem] before:top-0 before:w-px before:bg-border dark:before:bg-white/15\"\n >\n @for (child of item.children; track child.id) {\n <li\n class=\"transition-opacity duration-200 ease-out\"\n [class.opacity-0]=\"!isBranchOpen(item.id)\"\n [class.opacity-100]=\"isBranchOpen(item.id)\"\n >\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"child.disabled ? null : child.routerLink\"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n [class]=\"childRowClass(child)\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"childRowClass(child)\"\n [disabled]=\"!!child.disabled\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n </div>\n }\n\n @if (collapsed() && hoverId() === item.id) {\n <div\n class=\"absolute left-full top-0 z-50 ml-2 min-w-[11rem] overflow-hidden rounded-md border border-border bg-ink text-white shadow-lg dark:border-white/15\"\n role=\"tooltip\"\n >\n <div\n class=\"absolute inset-y-0 left-0 w-1\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n <div class=\"border-b border-white/10 px-3 py-2 pl-4\">\n <p class=\"m-0 text-body-sm font-semibold\">{{ item.label }}</p>\n </div>\n @if (item.children?.length) {\n <ul class=\"m-0 list-none space-y-0.5 p-1.5\">\n @for (child of item.children; track child.id) {\n <li>\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"\n child.disabled ? null : child.routerLink\n \"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n class=\"relative flex items-center gap-2 rounded px-2.5 py-1.5 text-body-sm text-white/85 no-underline transition-colors hover:bg-white/10 hover:text-white\"\n [class.opacity-50]=\"child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"relative flex w-full items-center gap-2 rounded px-2.5 py-1.5 text-left text-body-sm text-white/85 transition-colors hover:bg-white/10 hover:text-white\"\n [disabled]=\"!!child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n }\n </div>\n }\n </li>\n }\n </ul>\n </nav>\n\n <ng-content select=\"[footer]\" />\n </aside>\n\n <ng-template #rowInner let-item>\n <span class=\"inline-flex size-5 shrink-0 items-center justify-center\">\n @if (item.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"16\" />\n } @else {\n <span\n class=\"size-1.5 rounded-full bg-current opacity-70\"\n aria-hidden=\"true\"\n ></span>\n }\n </span>\n @if (!collapsed()) {\n <span class=\"min-w-0 flex-1 truncate text-left\">{{ item.label }}</span>\n @if (item.children?.length) {\n <africanies-icon\n name=\"chevron-down\"\n [size]=\"14\"\n class=\"shrink-0 opacity-70 transition-transform duration-200 ease-out\"\n [class.rotate-180]=\"isBranchOpen(item.id)\"\n />\n }\n }\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: item.badge,\n compact: collapsed(),\n }\"\n />\n </ng-template>\n\n <ng-template #navBadge let-visible=\"visible\" let-compact=\"compact\">\n @if (visible) {\n <span\n class=\"pointer-events-none absolute z-10 overflow-visible\"\n [class]=\"compact ? 'right-0.5 top-0.5 h-2 w-2' : 'right-1.5 top-1 h-2 w-2'\"\n aria-hidden=\"true\"\n >\n <span\n class=\"africanies-side-nav-badge-ping absolute left-1/2 top-1/2 size-2 rounded-full bg-danger\"\n ></span>\n <span\n class=\"absolute left-1/2 top-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-danger ring-2 ring-white dark:ring-ink\"\n ></span>\n </span>\n }\n </ng-template>", styles: ["@keyframes africanies-side-nav-badge-pulse{0%{transform:translate(-50%,-50%) scale(.85);opacity:.9}70%{transform:translate(-50%,-50%) scale(2.4);opacity:0}to{transform:translate(-50%,-50%) scale(2.4);opacity:0}}.africanies-side-nav-badge-ping{animation:africanies-side-nav-badge-pulse 1.4s cubic-bezier(0,0,.2,1) infinite}@media(prefers-reduced-motion:reduce){.africanies-side-nav-badge-ping{animation:none;opacity:.5}}\n"], dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14188
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: SideNavComponent, isStandalone: true, selector: "africanies-side-nav", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, activeId: { classPropertyName: "activeId", publicName: "activeId", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, expandParents: { classPropertyName: "expandParents", publicName: "expandParents", isSignal: true, isRequired: false, transformFunction: null }, expandBranchesByDefault: { classPropertyName: "expandBranchesByDefault", publicName: "expandBranchesByDefault", isSignal: true, isRequired: false, transformFunction: null }, linkActiveOptions: { classPropertyName: "linkActiveOptions", publicName: "linkActiveOptions", isSignal: true, isRequired: false, transformFunction: null }, showLogo: { classPropertyName: "showLogo", publicName: "showLogo", isSignal: true, isRequired: false, transformFunction: null }, logoSrc: { classPropertyName: "logoSrc", publicName: "logoSrc", isSignal: true, isRequired: false, transformFunction: null }, logoMiniSrc: { classPropertyName: "logoMiniSrc", publicName: "logoMiniSrc", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsed: "collapsedChange", activeId: "activeIdChange" }, host: { classAttribute: "relative z-20 block h-full min-h-0" }, ngImport: i0, template: " <aside\n class=\"relative flex h-full min-h-0 flex-col overflow-visible border-r border-border bg-surface transition-[width] duration-200 ease-out dark:border-white/10 dark:bg-ink-950\"\n [style.width]=\"collapsed() ? '3.75rem' : '15rem'\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n <div\n class=\"pointer-events-none absolute inset-y-0 left-0 w-[3px] transition-colors\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n\n <div\n class=\"sticky top-0 z-20 flex shrink-0 items-center gap-2 border-b border-border bg-surface py-3 dark:border-white/10 dark:bg-ink-950\"\n [class.flex-col]=\"collapsed()\"\n [class.px-3]=\"collapsed()\"\n [class.pl-4]=\"!collapsed()\"\n [class.pr-2]=\"!collapsed()\"\n >\n @if (collapsed()) {\n @if (showLogo()) {\n <img\n [src]=\"resolvedMiniLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"size-8 shrink-0 object-contain\"\n />\n }\n } @else {\n @if (showLogo()) {\n <div class=\"min-w-0 flex-1\">\n <img\n [src]=\"resolvedFullLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"h-9 max-w-full object-contain object-left\"\n />\n </div>\n } @else {\n <div\n class=\"min-w-0 flex-1 truncate text-body-sm font-semibold text-ink dark:text-white\"\n >\n <ng-content select=\"[brand]\" />\n </div>\n }\n }\n <button\n type=\"button\"\n class=\"inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white\"\n data-onboarding=\"nav-collapse\"\n [attr.aria-label]=\"\n collapsed() ? 'Expand navigation' : 'Collapse navigation'\n \"\n [attr.aria-expanded]=\"!collapsed()\"\n (click)=\"toggleCollapsed()\"\n >\n <africanies-icon\n [name]=\"collapsed() ? 'angle-double-right' : 'angle-double-left'\"\n [size]=\"16\"\n />\n </button>\n </div>\n\n <nav\n class=\"africanies-overlay-scroll relative min-h-0 flex-1 overflow-y-auto overflow-x-visible px-3 py-3\"\n >\n @if (!collapsed() && expandParents() && hasOpenBranches()) {\n <!-- Zero-height sticky host: keeps the control pinned without reserving a row. -->\n <div class=\"sticky top-0 z-10 h-0 overflow-visible\">\n <button\n type=\"button\"\n class=\"absolute right-0 top-0 inline-flex size-7 cursor-pointer items-center justify-center rounded-md bg-surface text-neutral-600 shadow-sm ring-1 ring-border-strong transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:bg-ink-950 dark:text-neutral-400 dark:ring-white/15 dark:hover:bg-white/10 dark:hover:text-white\"\n data-onboarding=\"nav-branches-collapse\"\n aria-label=\"Collapse all\"\n (click)=\"collapseAllBranches()\"\n >\n <africanies-icon name=\"compress\" [size]=\"14\" />\n </button>\n </div>\n }\n <ul class=\"m-0 flex list-none flex-col gap-0.5 p-0\">\n @for (item of items(); track item.id) {\n <li\n class=\"relative overflow-visible last:mb-0\"\n [class.mb-3]=\"!collapsed() && !item.children?.length\"\n (mouseenter)=\"onItemEnter(item.id)\"\n (mouseleave)=\"onItemLeave(item.id)\"\n >\n @if (item.routerLink !== null && item.routerLink !== undefined) {\n <a\n [routerLink]=\"item.disabled ? null : item.routerLink\"\n [queryParams]=\"item.queryParams\"\n [fragment]=\"item.fragment\"\n [class]=\"rowClass(item)\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n [attr.aria-disabled]=\"item.disabled ? true : null\"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"rowClass(item)\"\n [disabled]=\"!!item.disabled\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </button>\n }\n\n @if (!collapsed() && item.children?.length) {\n <div\n class=\"grid transition-[grid-template-rows] duration-200 ease-out\"\n [style.grid-template-rows]=\"\n isBranchOpen(item.id) ? '1fr' : '0fr'\n \"\n [attr.aria-hidden]=\"!isBranchOpen(item.id)\"\n [class.pointer-events-none]=\"!isBranchOpen(item.id)\"\n >\n <ul\n class=\"relative m-0 mt-0.5 min-h-0 list-none space-y-0.5 overflow-hidden py-1 pl-4 before:absolute before:bottom-1 before:left-[1.35rem] before:top-0 before:w-px before:bg-border dark:before:bg-white/15\"\n >\n @for (child of item.children; track child.id) {\n <li\n class=\"transition-opacity duration-200 ease-out\"\n [class.opacity-0]=\"!isBranchOpen(item.id)\"\n [class.opacity-100]=\"isBranchOpen(item.id)\"\n >\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"child.disabled ? null : child.routerLink\"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n [class]=\"childRowClass(child)\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"childRowClass(child)\"\n [disabled]=\"!!child.disabled\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n </div>\n }\n\n @if (collapsed() && hoverId() === item.id) {\n <div\n class=\"absolute left-full top-0 z-50 ml-2 min-w-[11rem] overflow-hidden rounded-md border border-border bg-ink text-white shadow-lg dark:border-white/15\"\n role=\"tooltip\"\n >\n <div\n class=\"absolute inset-y-0 left-0 w-1\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n <div class=\"border-b border-white/10 px-3 py-2 pl-4\">\n <p class=\"m-0 text-body-sm font-semibold\">{{ item.label }}</p>\n </div>\n @if (item.children?.length) {\n <ul class=\"m-0 list-none space-y-0.5 p-1.5\">\n @for (child of item.children; track child.id) {\n <li>\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"\n child.disabled ? null : child.routerLink\n \"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n class=\"relative flex items-center gap-2 rounded px-2.5 py-1.5 text-body-sm text-white/85 no-underline transition-colors hover:bg-white/10 hover:text-white\"\n [class.opacity-50]=\"child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"relative flex w-full items-center gap-2 rounded px-2.5 py-1.5 text-left text-body-sm text-white/85 transition-colors hover:bg-white/10 hover:text-white\"\n [disabled]=\"!!child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n }\n </div>\n }\n </li>\n }\n </ul>\n </nav>\n\n <ng-content select=\"[footer]\" />\n </aside>\n\n <ng-template #rowInner let-item>\n <span class=\"inline-flex size-5 shrink-0 items-center justify-center\">\n @if (item.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"16\" />\n } @else {\n <span\n class=\"size-1.5 rounded-full bg-current opacity-70\"\n aria-hidden=\"true\"\n ></span>\n }\n </span>\n @if (!collapsed()) {\n <span class=\"min-w-0 flex-1 truncate text-left\">{{ item.label }}</span>\n @if (item.children?.length) {\n <africanies-icon\n name=\"chevron-down\"\n [size]=\"14\"\n class=\"shrink-0 opacity-70 transition-transform duration-200 ease-out\"\n [class.rotate-180]=\"isBranchOpen(item.id)\"\n />\n }\n }\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: item.badge,\n compact: collapsed(),\n }\"\n />\n </ng-template>\n\n <ng-template #navBadge let-visible=\"visible\" let-compact=\"compact\">\n @if (visible) {\n <span\n class=\"pointer-events-none absolute z-10 overflow-visible\"\n [class]=\"compact ? 'right-0.5 top-0.5 h-2 w-2' : 'right-1.5 top-1 h-2 w-2'\"\n aria-hidden=\"true\"\n >\n <span\n class=\"africanies-side-nav-badge-ping absolute left-1/2 top-1/2 size-2 rounded-full bg-danger\"\n ></span>\n <span\n class=\"absolute left-1/2 top-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-danger ring-2 ring-white dark:ring-ink\"\n ></span>\n </span>\n }\n </ng-template>", styles: ["@keyframes africanies-side-nav-badge-pulse{0%{transform:translate(-50%,-50%) scale(.85);opacity:.9}70%{transform:translate(-50%,-50%) scale(2.4);opacity:0}to{transform:translate(-50%,-50%) scale(2.4);opacity:0}}.africanies-side-nav-badge-ping{animation:africanies-side-nav-badge-pulse 1.4s cubic-bezier(0,0,.2,1) infinite}@media(prefers-reduced-motion:reduce){.africanies-side-nav-badge-ping{animation:none;opacity:.5}}\n"], dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14113
14189
  }
14114
14190
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SideNavComponent, decorators: [{
14115
14191
  type: Component,
14116
14192
  args: [{ selector: 'africanies-side-nav', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AfricaniesIconComponent, RouterLink, NgTemplateOutlet], host: {
14117
- class: 'relative z-20 block h-full',
14118
- }, template: " <aside\n class=\"relative flex h-full flex-col overflow-visible border-r border-border bg-white transition-[width] duration-200 ease-out dark:border-white/10 dark:bg-ink-950\"\n [style.width]=\"collapsed() ? '3.75rem' : '15rem'\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n <div\n class=\"pointer-events-none absolute inset-y-0 left-0 w-[3px] transition-colors\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n\n <div\n class=\"flex shrink-0 items-center gap-2 border-b border-border py-3 dark:border-white/10\"\n [class.flex-col]=\"collapsed()\"\n [class.px-3]=\"collapsed()\"\n [class.pl-4]=\"!collapsed()\"\n [class.pr-2]=\"!collapsed()\"\n >\n @if (collapsed()) {\n @if (showLogo()) {\n <img\n [src]=\"resolvedMiniLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"size-8 shrink-0 object-contain\"\n />\n }\n } @else {\n @if (showLogo()) {\n <div class=\"min-w-0 flex-1\">\n <img\n [src]=\"resolvedFullLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"h-9 max-w-full object-contain object-left\"\n />\n </div>\n } @else {\n <div\n class=\"min-w-0 flex-1 truncate text-body-sm font-semibold text-ink dark:text-white\"\n >\n <ng-content select=\"[brand]\" />\n </div>\n }\n }\n <button\n type=\"button\"\n class=\"inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white\"\n [attr.aria-label]=\"\n collapsed() ? 'Expand navigation' : 'Collapse navigation'\n \"\n [attr.aria-expanded]=\"!collapsed()\"\n (click)=\"toggleCollapsed()\"\n >\n <africanies-icon\n [name]=\"collapsed() ? 'angle-double-right' : 'angle-double-left'\"\n [size]=\"16\"\n />\n </button>\n </div>\n\n <nav class=\"africanies-overlay-scroll relative flex-1 overflow-y-auto overflow-x-visible px-3 py-3\">\n @if (!collapsed() && expandParents() && hasOpenBranches()) {\n <button\n type=\"button\"\n class=\"absolute right-1.5 top-1.5 z-10 inline-flex size-7 cursor-pointer items-center justify-center rounded-md bg-white text-neutral-500 shadow-sm ring-1 ring-border/80 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:bg-ink-950 dark:text-neutral-400 dark:ring-white/15 dark:hover:bg-white/10 dark:hover:text-white\"\n aria-label=\"Collapse all\"\n (click)=\"collapseAllBranches()\"\n >\n <africanies-icon name=\"compress\" [size]=\"14\" />\n </button>\n }\n <ul class=\"m-0 flex list-none flex-col gap-0.5 p-0\">\n @for (item of items(); track item.id) {\n <li\n class=\"relative overflow-visible\"\n (mouseenter)=\"onItemEnter(item.id)\"\n (mouseleave)=\"onItemLeave(item.id)\"\n >\n @if (item.routerLink !== null && item.routerLink !== undefined) {\n <a\n [routerLink]=\"item.disabled ? null : item.routerLink\"\n [queryParams]=\"item.queryParams\"\n [fragment]=\"item.fragment\"\n [class]=\"rowClass(item)\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n [attr.aria-disabled]=\"item.disabled ? true : null\"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"rowClass(item)\"\n [disabled]=\"!!item.disabled\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </button>\n }\n\n @if (!collapsed() && item.children?.length) {\n <div\n class=\"grid transition-[grid-template-rows] duration-200 ease-out\"\n [style.grid-template-rows]=\"\n isBranchOpen(item.id) ? '1fr' : '0fr'\n \"\n [attr.aria-hidden]=\"!isBranchOpen(item.id)\"\n [class.pointer-events-none]=\"!isBranchOpen(item.id)\"\n >\n <ul\n class=\"relative m-0 mt-0.5 min-h-0 list-none space-y-0.5 overflow-hidden py-1 pl-4 before:absolute before:bottom-1 before:left-[1.35rem] before:top-0 before:w-px before:bg-border dark:before:bg-white/15\"\n >\n @for (child of item.children; track child.id) {\n <li\n class=\"transition-opacity duration-200 ease-out\"\n [class.opacity-0]=\"!isBranchOpen(item.id)\"\n [class.opacity-100]=\"isBranchOpen(item.id)\"\n >\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"child.disabled ? null : child.routerLink\"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n [class]=\"childRowClass(child)\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"childRowClass(child)\"\n [disabled]=\"!!child.disabled\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n </div>\n }\n\n @if (collapsed() && hoverId() === item.id) {\n <div\n class=\"absolute left-full top-0 z-50 ml-2 min-w-[11rem] overflow-hidden rounded-md border border-border bg-ink text-white shadow-lg dark:border-white/15\"\n role=\"tooltip\"\n >\n <div\n class=\"absolute inset-y-0 left-0 w-1\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n <div class=\"border-b border-white/10 px-3 py-2 pl-4\">\n <p class=\"m-0 text-body-sm font-semibold\">{{ item.label }}</p>\n </div>\n @if (item.children?.length) {\n <ul class=\"m-0 list-none space-y-0.5 p-1.5\">\n @for (child of item.children; track child.id) {\n <li>\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"\n child.disabled ? null : child.routerLink\n \"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n class=\"relative flex items-center gap-2 rounded px-2.5 py-1.5 text-body-sm text-white/85 no-underline transition-colors hover:bg-white/10 hover:text-white\"\n [class.opacity-50]=\"child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"relative flex w-full items-center gap-2 rounded px-2.5 py-1.5 text-left text-body-sm text-white/85 transition-colors hover:bg-white/10 hover:text-white\"\n [disabled]=\"!!child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n }\n </div>\n }\n </li>\n }\n </ul>\n </nav>\n\n <ng-content select=\"[footer]\" />\n </aside>\n\n <ng-template #rowInner let-item>\n <span class=\"inline-flex size-5 shrink-0 items-center justify-center\">\n @if (item.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"16\" />\n } @else {\n <span\n class=\"size-1.5 rounded-full bg-current opacity-70\"\n aria-hidden=\"true\"\n ></span>\n }\n </span>\n @if (!collapsed()) {\n <span class=\"min-w-0 flex-1 truncate text-left\">{{ item.label }}</span>\n @if (item.children?.length) {\n <africanies-icon\n name=\"chevron-down\"\n [size]=\"14\"\n class=\"shrink-0 opacity-70 transition-transform duration-200 ease-out\"\n [class.rotate-180]=\"isBranchOpen(item.id)\"\n />\n }\n }\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: item.badge,\n compact: collapsed(),\n }\"\n />\n </ng-template>\n\n <ng-template #navBadge let-visible=\"visible\" let-compact=\"compact\">\n @if (visible) {\n <span\n class=\"pointer-events-none absolute z-10 overflow-visible\"\n [class]=\"compact ? 'right-0.5 top-0.5 h-2 w-2' : 'right-1.5 top-1 h-2 w-2'\"\n aria-hidden=\"true\"\n >\n <span\n class=\"africanies-side-nav-badge-ping absolute left-1/2 top-1/2 size-2 rounded-full bg-danger\"\n ></span>\n <span\n class=\"absolute left-1/2 top-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-danger ring-2 ring-white dark:ring-ink\"\n ></span>\n </span>\n }\n </ng-template>", styles: ["@keyframes africanies-side-nav-badge-pulse{0%{transform:translate(-50%,-50%) scale(.85);opacity:.9}70%{transform:translate(-50%,-50%) scale(2.4);opacity:0}to{transform:translate(-50%,-50%) scale(2.4);opacity:0}}.africanies-side-nav-badge-ping{animation:africanies-side-nav-badge-pulse 1.4s cubic-bezier(0,0,.2,1) infinite}@media(prefers-reduced-motion:reduce){.africanies-side-nav-badge-ping{animation:none;opacity:.5}}\n"] }]
14193
+ class: 'relative z-20 block h-full min-h-0',
14194
+ }, template: " <aside\n class=\"relative flex h-full min-h-0 flex-col overflow-visible border-r border-border bg-surface transition-[width] duration-200 ease-out dark:border-white/10 dark:bg-ink-950\"\n [style.width]=\"collapsed() ? '3.75rem' : '15rem'\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n <div\n class=\"pointer-events-none absolute inset-y-0 left-0 w-[3px] transition-colors\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n\n <div\n class=\"sticky top-0 z-20 flex shrink-0 items-center gap-2 border-b border-border bg-surface py-3 dark:border-white/10 dark:bg-ink-950\"\n [class.flex-col]=\"collapsed()\"\n [class.px-3]=\"collapsed()\"\n [class.pl-4]=\"!collapsed()\"\n [class.pr-2]=\"!collapsed()\"\n >\n @if (collapsed()) {\n @if (showLogo()) {\n <img\n [src]=\"resolvedMiniLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"size-8 shrink-0 object-contain\"\n />\n }\n } @else {\n @if (showLogo()) {\n <div class=\"min-w-0 flex-1\">\n <img\n [src]=\"resolvedFullLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"h-9 max-w-full object-contain object-left\"\n />\n </div>\n } @else {\n <div\n class=\"min-w-0 flex-1 truncate text-body-sm font-semibold text-ink dark:text-white\"\n >\n <ng-content select=\"[brand]\" />\n </div>\n }\n }\n <button\n type=\"button\"\n class=\"inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white\"\n data-onboarding=\"nav-collapse\"\n [attr.aria-label]=\"\n collapsed() ? 'Expand navigation' : 'Collapse navigation'\n \"\n [attr.aria-expanded]=\"!collapsed()\"\n (click)=\"toggleCollapsed()\"\n >\n <africanies-icon\n [name]=\"collapsed() ? 'angle-double-right' : 'angle-double-left'\"\n [size]=\"16\"\n />\n </button>\n </div>\n\n <nav\n class=\"africanies-overlay-scroll relative min-h-0 flex-1 overflow-y-auto overflow-x-visible px-3 py-3\"\n >\n @if (!collapsed() && expandParents() && hasOpenBranches()) {\n <!-- Zero-height sticky host: keeps the control pinned without reserving a row. -->\n <div class=\"sticky top-0 z-10 h-0 overflow-visible\">\n <button\n type=\"button\"\n class=\"absolute right-0 top-0 inline-flex size-7 cursor-pointer items-center justify-center rounded-md bg-surface text-neutral-600 shadow-sm ring-1 ring-border-strong transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:bg-ink-950 dark:text-neutral-400 dark:ring-white/15 dark:hover:bg-white/10 dark:hover:text-white\"\n data-onboarding=\"nav-branches-collapse\"\n aria-label=\"Collapse all\"\n (click)=\"collapseAllBranches()\"\n >\n <africanies-icon name=\"compress\" [size]=\"14\" />\n </button>\n </div>\n }\n <ul class=\"m-0 flex list-none flex-col gap-0.5 p-0\">\n @for (item of items(); track item.id) {\n <li\n class=\"relative overflow-visible last:mb-0\"\n [class.mb-3]=\"!collapsed() && !item.children?.length\"\n (mouseenter)=\"onItemEnter(item.id)\"\n (mouseleave)=\"onItemLeave(item.id)\"\n >\n @if (item.routerLink !== null && item.routerLink !== undefined) {\n <a\n [routerLink]=\"item.disabled ? null : item.routerLink\"\n [queryParams]=\"item.queryParams\"\n [fragment]=\"item.fragment\"\n [class]=\"rowClass(item)\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n [attr.aria-disabled]=\"item.disabled ? true : null\"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"rowClass(item)\"\n [disabled]=\"!!item.disabled\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </button>\n }\n\n @if (!collapsed() && item.children?.length) {\n <div\n class=\"grid transition-[grid-template-rows] duration-200 ease-out\"\n [style.grid-template-rows]=\"\n isBranchOpen(item.id) ? '1fr' : '0fr'\n \"\n [attr.aria-hidden]=\"!isBranchOpen(item.id)\"\n [class.pointer-events-none]=\"!isBranchOpen(item.id)\"\n >\n <ul\n class=\"relative m-0 mt-0.5 min-h-0 list-none space-y-0.5 overflow-hidden py-1 pl-4 before:absolute before:bottom-1 before:left-[1.35rem] before:top-0 before:w-px before:bg-border dark:before:bg-white/15\"\n >\n @for (child of item.children; track child.id) {\n <li\n class=\"transition-opacity duration-200 ease-out\"\n [class.opacity-0]=\"!isBranchOpen(item.id)\"\n [class.opacity-100]=\"isBranchOpen(item.id)\"\n >\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"child.disabled ? null : child.routerLink\"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n [class]=\"childRowClass(child)\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"childRowClass(child)\"\n [disabled]=\"!!child.disabled\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n </div>\n }\n\n @if (collapsed() && hoverId() === item.id) {\n <div\n class=\"absolute left-full top-0 z-50 ml-2 min-w-[11rem] overflow-hidden rounded-md border border-border bg-ink text-white shadow-lg dark:border-white/15\"\n role=\"tooltip\"\n >\n <div\n class=\"absolute inset-y-0 left-0 w-1\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n <div class=\"border-b border-white/10 px-3 py-2 pl-4\">\n <p class=\"m-0 text-body-sm font-semibold\">{{ item.label }}</p>\n </div>\n @if (item.children?.length) {\n <ul class=\"m-0 list-none space-y-0.5 p-1.5\">\n @for (child of item.children; track child.id) {\n <li>\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"\n child.disabled ? null : child.routerLink\n \"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n class=\"relative flex items-center gap-2 rounded px-2.5 py-1.5 text-body-sm text-white/85 no-underline transition-colors hover:bg-white/10 hover:text-white\"\n [class.opacity-50]=\"child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"relative flex w-full items-center gap-2 rounded px-2.5 py-1.5 text-left text-body-sm text-white/85 transition-colors hover:bg-white/10 hover:text-white\"\n [disabled]=\"!!child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n }\n </div>\n }\n </li>\n }\n </ul>\n </nav>\n\n <ng-content select=\"[footer]\" />\n </aside>\n\n <ng-template #rowInner let-item>\n <span class=\"inline-flex size-5 shrink-0 items-center justify-center\">\n @if (item.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"16\" />\n } @else {\n <span\n class=\"size-1.5 rounded-full bg-current opacity-70\"\n aria-hidden=\"true\"\n ></span>\n }\n </span>\n @if (!collapsed()) {\n <span class=\"min-w-0 flex-1 truncate text-left\">{{ item.label }}</span>\n @if (item.children?.length) {\n <africanies-icon\n name=\"chevron-down\"\n [size]=\"14\"\n class=\"shrink-0 opacity-70 transition-transform duration-200 ease-out\"\n [class.rotate-180]=\"isBranchOpen(item.id)\"\n />\n }\n }\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: item.badge,\n compact: collapsed(),\n }\"\n />\n </ng-template>\n\n <ng-template #navBadge let-visible=\"visible\" let-compact=\"compact\">\n @if (visible) {\n <span\n class=\"pointer-events-none absolute z-10 overflow-visible\"\n [class]=\"compact ? 'right-0.5 top-0.5 h-2 w-2' : 'right-1.5 top-1 h-2 w-2'\"\n aria-hidden=\"true\"\n >\n <span\n class=\"africanies-side-nav-badge-ping absolute left-1/2 top-1/2 size-2 rounded-full bg-danger\"\n ></span>\n <span\n class=\"absolute left-1/2 top-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-danger ring-2 ring-white dark:ring-ink\"\n ></span>\n </span>\n }\n </ng-template>", styles: ["@keyframes africanies-side-nav-badge-pulse{0%{transform:translate(-50%,-50%) scale(.85);opacity:.9}70%{transform:translate(-50%,-50%) scale(2.4);opacity:0}to{transform:translate(-50%,-50%) scale(2.4);opacity:0}}.africanies-side-nav-badge-ping{animation:africanies-side-nav-badge-pulse 1.4s cubic-bezier(0,0,.2,1) infinite}@media(prefers-reduced-motion:reduce){.africanies-side-nav-badge-ping{animation:none;opacity:.5}}\n"] }]
14119
14195
  }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }, { type: i0.Output, args: ["collapsedChange"] }], activeId: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeId", required: false }] }, { type: i0.Output, args: ["activeIdChange"] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], expandParents: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandParents", required: false }] }], expandBranchesByDefault: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandBranchesByDefault", required: false }] }], linkActiveOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "linkActiveOptions", required: false }] }], showLogo: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLogo", required: false }] }], logoSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "logoSrc", required: false }] }], logoMiniSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "logoMiniSrc", required: false }] }] } });
14120
14196
 
14121
14197
  /**
@@ -14644,7 +14720,7 @@ class AppShellContentHeaderComponent {
14644
14720
  <div class="flex min-w-0 items-center gap-2 sm:gap-3">
14645
14721
  @if (showBackButton()) {
14646
14722
  <a
14647
- class="inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-lg text-neutral-600 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
14723
+ class="inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-lg text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
14648
14724
  [attr.aria-label]="backLabel()"
14649
14725
  [routerLink]="backTarget()!.routerLink"
14650
14726
  [queryParams]="backTarget()!.queryParams"
@@ -14690,7 +14766,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
14690
14766
  <div class="flex min-w-0 items-center gap-2 sm:gap-3">
14691
14767
  @if (showBackButton()) {
14692
14768
  <a
14693
- class="inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-lg text-neutral-600 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
14769
+ class="inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-lg text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
14694
14770
  [attr.aria-label]="backLabel()"
14695
14771
  [routerLink]="backTarget()!.routerLink"
14696
14772
  [queryParams]="backTarget()!.queryParams"
@@ -14863,7 +14939,7 @@ class AvatarMenuComponent {
14863
14939
  <button
14864
14940
  type="button"
14865
14941
  africaniesActionMenuTrigger
14866
- class="inline-flex cursor-pointer items-center gap-1 rounded-lg p-0.5 pr-1 transition-colors hover:bg-background-welcome focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink disabled:cursor-not-allowed disabled:opacity-50 dark:hover:bg-white/10"
14942
+ class="inline-flex cursor-pointer items-center gap-1 rounded-lg p-0.5 pr-1 transition-colors hover:bg-background-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus disabled:cursor-not-allowed disabled:opacity-50 dark:hover:bg-white/10"
14867
14943
  [attr.aria-label]="ariaLabel()"
14868
14944
  >
14869
14945
  <africanies-avatar [name]="name()" [src]="src()" [size]="size()" />
@@ -14871,7 +14947,7 @@ class AvatarMenuComponent {
14871
14947
  <africanies-icon
14872
14948
  name="chevron-down"
14873
14949
  [size]="14"
14874
- class="shrink-0 text-neutral-500 dark:text-neutral-400"
14950
+ class="shrink-0 text-neutral-600 dark:text-neutral-400"
14875
14951
  aria-hidden="true"
14876
14952
  />
14877
14953
  }
@@ -14900,7 +14976,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
14900
14976
  <button
14901
14977
  type="button"
14902
14978
  africaniesActionMenuTrigger
14903
- class="inline-flex cursor-pointer items-center gap-1 rounded-lg p-0.5 pr-1 transition-colors hover:bg-background-welcome focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink disabled:cursor-not-allowed disabled:opacity-50 dark:hover:bg-white/10"
14979
+ class="inline-flex cursor-pointer items-center gap-1 rounded-lg p-0.5 pr-1 transition-colors hover:bg-background-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus disabled:cursor-not-allowed disabled:opacity-50 dark:hover:bg-white/10"
14904
14980
  [attr.aria-label]="ariaLabel()"
14905
14981
  >
14906
14982
  <africanies-avatar [name]="name()" [src]="src()" [size]="size()" />
@@ -14908,7 +14984,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
14908
14984
  <africanies-icon
14909
14985
  name="chevron-down"
14910
14986
  [size]="14"
14911
- class="shrink-0 text-neutral-500 dark:text-neutral-400"
14987
+ class="shrink-0 text-neutral-600 dark:text-neutral-400"
14912
14988
  aria-hidden="true"
14913
14989
  />
14914
14990
  }
@@ -14941,9 +15017,9 @@ class NotificationDrawerPanel {
14941
15017
  viewLinkClass = computed(() => {
14942
15018
  const base = 'inline-flex cursor-pointer items-center gap-1 self-start text-caption font-semibold underline underline-offset-[3px] transition-colors';
14943
15019
  if (this.shipping.mode() === 'stn') {
14944
- return `${base} text-import decoration-import/35 hover:decoration-import dark:text-import-light dark:decoration-import-light/40 dark:hover:decoration-import-light`;
15020
+ return `${base} text-import-strong decoration-import-strong/35 hover:decoration-import-strong dark:text-import-light dark:decoration-import-light/40 dark:hover:decoration-import-light`;
14945
15021
  }
14946
- return `${base} text-export decoration-export/35 hover:decoration-export dark:text-export-light dark:decoration-export-light/40 dark:hover:decoration-export-light`;
15022
+ return `${base} text-export-strong decoration-export-strong/35 hover:decoration-export-strong dark:text-export-light dark:decoration-export-light/40 dark:hover:decoration-export-light`;
14947
15023
  }, /* @ts-ignore */
14948
15024
  ...(ngDevMode ? [{ debugName: "viewLinkClass" }] : /* istanbul ignore next */ []));
14949
15025
  title = computed(() => this.data.title ?? 'Notifications', /* @ts-ignore */
@@ -15289,7 +15365,7 @@ class NotificationDrawerPanel {
15289
15365
  {{ title() }}
15290
15366
  </h2>
15291
15367
  @if (hasUnread()) {
15292
- <p class="m-0 text-caption text-neutral-500 dark:text-neutral-400">
15368
+ <p class="m-0 text-caption text-neutral-600 dark:text-neutral-400">
15293
15369
  {{ unreadCount() }} unread
15294
15370
  </p>
15295
15371
  }
@@ -15335,7 +15411,7 @@ class NotificationDrawerPanel {
15335
15411
  <africanies-icon
15336
15412
  name="spinner"
15337
15413
  [size]="24"
15338
- class="animate-spin text-neutral-500 dark:text-neutral-400"
15414
+ class="animate-spin text-neutral-600 dark:text-neutral-400"
15339
15415
  />
15340
15416
  <p class="m-0 text-body-sm text-neutral-600 dark:text-neutral-400">
15341
15417
  Loading notifications…
@@ -15402,7 +15478,7 @@ class NotificationDrawerPanel {
15402
15478
  </h3>
15403
15479
  @if (item.timestamp) {
15404
15480
  <time
15405
- class="shrink-0 whitespace-nowrap text-[11px] tabular-nums text-neutral-500 dark:text-neutral-400"
15481
+ class="shrink-0 whitespace-nowrap text-[11px] tabular-nums text-neutral-600 dark:text-neutral-400"
15406
15482
  [dateTime]="item.timestamp"
15407
15483
  >
15408
15484
  {{ formatTimestamp(item.timestamp) }}
@@ -15481,7 +15557,7 @@ class NotificationDrawerPanel {
15481
15557
  >
15482
15558
  @if (loadingMore()) {
15483
15559
  <span
15484
- class="inline-flex items-center gap-2 text-caption text-neutral-500 dark:text-neutral-400"
15560
+ class="inline-flex items-center gap-2 text-caption text-neutral-600 dark:text-neutral-400"
15485
15561
  aria-live="polite"
15486
15562
  >
15487
15563
  <africanies-icon name="spinner" [size]="14" class="animate-spin" />
@@ -15520,7 +15596,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
15520
15596
  {{ title() }}
15521
15597
  </h2>
15522
15598
  @if (hasUnread()) {
15523
- <p class="m-0 text-caption text-neutral-500 dark:text-neutral-400">
15599
+ <p class="m-0 text-caption text-neutral-600 dark:text-neutral-400">
15524
15600
  {{ unreadCount() }} unread
15525
15601
  </p>
15526
15602
  }
@@ -15566,7 +15642,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
15566
15642
  <africanies-icon
15567
15643
  name="spinner"
15568
15644
  [size]="24"
15569
- class="animate-spin text-neutral-500 dark:text-neutral-400"
15645
+ class="animate-spin text-neutral-600 dark:text-neutral-400"
15570
15646
  />
15571
15647
  <p class="m-0 text-body-sm text-neutral-600 dark:text-neutral-400">
15572
15648
  Loading notifications…
@@ -15633,7 +15709,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
15633
15709
  </h3>
15634
15710
  @if (item.timestamp) {
15635
15711
  <time
15636
- class="shrink-0 whitespace-nowrap text-[11px] tabular-nums text-neutral-500 dark:text-neutral-400"
15712
+ class="shrink-0 whitespace-nowrap text-[11px] tabular-nums text-neutral-600 dark:text-neutral-400"
15637
15713
  [dateTime]="item.timestamp"
15638
15714
  >
15639
15715
  {{ formatTimestamp(item.timestamp) }}
@@ -15712,7 +15788,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
15712
15788
  >
15713
15789
  @if (loadingMore()) {
15714
15790
  <span
15715
- class="inline-flex items-center gap-2 text-caption text-neutral-500 dark:text-neutral-400"
15791
+ class="inline-flex items-center gap-2 text-caption text-neutral-600 dark:text-neutral-400"
15716
15792
  aria-live="polite"
15717
15793
  >
15718
15794
  <africanies-icon name="spinner" [size]="14" class="animate-spin" />
@@ -16469,7 +16545,7 @@ class AppShellHeaderComponent {
16469
16545
  @if (greeting(); as hello) {
16470
16546
  <div class="min-w-0 overflow-hidden">
16471
16547
  <p
16472
- class="m-0 truncate text-caption leading-none text-neutral-500 dark:text-neutral-400"
16548
+ class="m-0 truncate text-caption leading-none text-neutral-600 dark:text-neutral-400"
16473
16549
  >
16474
16550
  {{ hello.kicker }}
16475
16551
  </p>
@@ -16495,7 +16571,7 @@ class AppShellHeaderComponent {
16495
16571
  <africanies-icon
16496
16572
  [name]="weatherIcon()"
16497
16573
  [size]="18"
16498
- class="text-neutral-500 dark:text-neutral-400"
16574
+ class="text-neutral-600 dark:text-neutral-400"
16499
16575
  />
16500
16576
  @if (weatherTemp(); as temp) {
16501
16577
  <span
@@ -16505,7 +16581,7 @@ class AppShellHeaderComponent {
16505
16581
  </span>
16506
16582
  }
16507
16583
  <span
16508
- class="hidden text-caption text-neutral-500 md:inline dark:text-neutral-400"
16584
+ class="hidden text-caption text-neutral-600 md:inline dark:text-neutral-400"
16509
16585
  >
16510
16586
  {{ weatherPlace() }}
16511
16587
  </span>
@@ -16515,7 +16591,7 @@ class AppShellHeaderComponent {
16515
16591
  @if (showClock()) {
16516
16592
  <div class="flex items-baseline gap-2" aria-live="polite">
16517
16593
  <time
16518
- class="hidden text-caption text-neutral-500 md:inline dark:text-neutral-400"
16594
+ class="hidden text-caption text-neutral-600 md:inline dark:text-neutral-400"
16519
16595
  [dateTime]="nowIso()"
16520
16596
  >
16521
16597
  {{ now() | date: clockDateFormat() }}
@@ -16545,7 +16621,7 @@ class AppShellHeaderComponent {
16545
16621
  @if (showNotifications()) {
16546
16622
  <button
16547
16623
  type="button"
16548
- class="relative inline-flex size-9 cursor-pointer items-center justify-center rounded-lg text-neutral-600 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
16624
+ class="relative inline-flex size-9 cursor-pointer items-center justify-center rounded-lg text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
16549
16625
  [attr.aria-label]="notificationAriaLabel()"
16550
16626
  (click)="openNotifications()"
16551
16627
  >
@@ -16596,7 +16672,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16596
16672
  @if (greeting(); as hello) {
16597
16673
  <div class="min-w-0 overflow-hidden">
16598
16674
  <p
16599
- class="m-0 truncate text-caption leading-none text-neutral-500 dark:text-neutral-400"
16675
+ class="m-0 truncate text-caption leading-none text-neutral-600 dark:text-neutral-400"
16600
16676
  >
16601
16677
  {{ hello.kicker }}
16602
16678
  </p>
@@ -16622,7 +16698,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16622
16698
  <africanies-icon
16623
16699
  [name]="weatherIcon()"
16624
16700
  [size]="18"
16625
- class="text-neutral-500 dark:text-neutral-400"
16701
+ class="text-neutral-600 dark:text-neutral-400"
16626
16702
  />
16627
16703
  @if (weatherTemp(); as temp) {
16628
16704
  <span
@@ -16632,7 +16708,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16632
16708
  </span>
16633
16709
  }
16634
16710
  <span
16635
- class="hidden text-caption text-neutral-500 md:inline dark:text-neutral-400"
16711
+ class="hidden text-caption text-neutral-600 md:inline dark:text-neutral-400"
16636
16712
  >
16637
16713
  {{ weatherPlace() }}
16638
16714
  </span>
@@ -16642,7 +16718,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16642
16718
  @if (showClock()) {
16643
16719
  <div class="flex items-baseline gap-2" aria-live="polite">
16644
16720
  <time
16645
- class="hidden text-caption text-neutral-500 md:inline dark:text-neutral-400"
16721
+ class="hidden text-caption text-neutral-600 md:inline dark:text-neutral-400"
16646
16722
  [dateTime]="nowIso()"
16647
16723
  >
16648
16724
  {{ now() | date: clockDateFormat() }}
@@ -16672,7 +16748,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16672
16748
  @if (showNotifications()) {
16673
16749
  <button
16674
16750
  type="button"
16675
- class="relative inline-flex size-9 cursor-pointer items-center justify-center rounded-lg text-neutral-600 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
16751
+ class="relative inline-flex size-9 cursor-pointer items-center justify-center rounded-lg text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
16676
16752
  [attr.aria-label]="notificationAriaLabel()"
16677
16753
  (click)="openNotifications()"
16678
16754
  >
@@ -16874,14 +16950,14 @@ class AppShellComponent {
16874
16950
  // Preview embeds: absolute/h-full so the drawer stays inside the frame.
16875
16951
  // Production: h-dvh so the rail fills the viewport (h-full only matches parent).
16876
16952
  if (preview === 'desktop') {
16877
- return 'z-50 shrink-0 self-start sticky top-0 block h-full';
16953
+ return 'z-50 shrink-0 self-start sticky top-0 block h-full min-h-0';
16878
16954
  }
16879
16955
  if (preview === 'mobile' || preview === 'tablet') {
16880
16956
  return open
16881
- ? 'z-50 shrink-0 self-start absolute inset-y-0 left-0 h-full shadow-xl'
16957
+ ? 'z-50 shrink-0 self-start absolute inset-y-0 left-0 h-full min-h-0 shadow-xl'
16882
16958
  : 'hidden';
16883
16959
  }
16884
- const base = 'z-50 shrink-0 self-start h-dvh';
16960
+ const base = 'z-50 shrink-0 self-start h-dvh min-h-0';
16885
16961
  if (open) {
16886
16962
  return `${base} fixed inset-y-0 left-0 shadow-xl lg:sticky lg:top-0 lg:block`;
16887
16963
  }
@@ -16917,13 +16993,13 @@ class AppShellComponent {
16917
16993
  this.mobileNavOpen.set(false);
16918
16994
  }
16919
16995
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AppShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
16920
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AppShellComponent, isStandalone: true, selector: "africanies-app-shell", inputs: { contentWidth: { classPropertyName: "contentWidth", publicName: "contentWidth", isSignal: true, isRequired: false, transformFunction: null }, showAccent: { classPropertyName: "showAccent", publicName: "showAccent", isSignal: true, isRequired: false, transformFunction: null }, showHeaderChrome: { classPropertyName: "showHeaderChrome", publicName: "showHeaderChrome", isSignal: true, isRequired: false, transformFunction: null }, layoutPreview: { classPropertyName: "layoutPreview", publicName: "layoutPreview", isSignal: true, isRequired: false, transformFunction: null }, headerTitle: { classPropertyName: "headerTitle", publicName: "headerTitle", isSignal: true, isRequired: false, transformFunction: null }, headerSubtitle: { classPropertyName: "headerSubtitle", publicName: "headerSubtitle", isSignal: true, isRequired: false, transformFunction: null }, breadcrumbs: { classPropertyName: "breadcrumbs", publicName: "breadcrumbs", isSignal: true, isRequired: false, transformFunction: null }, catalogNav: { classPropertyName: "catalogNav", publicName: "catalogNav", isSignal: true, isRequired: false, transformFunction: null }, showContentBack: { classPropertyName: "showContentBack", publicName: "showContentBack", isSignal: true, isRequired: false, transformFunction: null }, contentBackLink: { classPropertyName: "contentBackLink", publicName: "contentBackLink", isSignal: true, isRequired: false, transformFunction: null }, contentBackLabel: { classPropertyName: "contentBackLabel", publicName: "contentBackLabel", isSignal: true, isRequired: false, transformFunction: null }, userName: { classPropertyName: "userName", publicName: "userName", isSignal: true, isRequired: false, transformFunction: null }, greetingName: { classPropertyName: "greetingName", publicName: "greetingName", isSignal: true, isRequired: false, transformFunction: null }, userAvatarSrc: { classPropertyName: "userAvatarSrc", publicName: "userAvatarSrc", isSignal: true, isRequired: false, transformFunction: null }, userMenuItems: { classPropertyName: "userMenuItems", publicName: "userMenuItems", isSignal: true, isRequired: false, transformFunction: null }, notifications: { classPropertyName: "notifications", publicName: "notifications", isSignal: true, isRequired: false, transformFunction: null }, notificationsTitle: { classPropertyName: "notificationsTitle", publicName: "notificationsTitle", isSignal: true, isRequired: false, transformFunction: null }, showClock: { classPropertyName: "showClock", publicName: "showClock", isSignal: true, isRequired: false, transformFunction: null }, showNotifications: { classPropertyName: "showNotifications", publicName: "showNotifications", isSignal: true, isRequired: false, transformFunction: null }, clockDateFormat: { classPropertyName: "clockDateFormat", publicName: "clockDateFormat", isSignal: true, isRequired: false, transformFunction: null }, clockFormat: { classPropertyName: "clockFormat", publicName: "clockFormat", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClass()" } }, queries: [{ propertyName: "customHeader", first: true, predicate: AppShellHeaderSlotDirective, descendants: true, isSignal: true }], ngImport: i0, template: " <div [class]=\"shellRootClass()\">\n @if (mobileNavOpen()) {\n <button\n type=\"button\"\n class=\"absolute inset-0 z-40 cursor-pointer bg-ink/40\"\n [class.lg:hidden]=\"!layoutPreview()\"\n aria-label=\"Close navigation\"\n (click)=\"closeMobileNav()\"\n ></button>\n }\n\n <div [class]=\"sidenavHostClass()\">\n <ng-content select=\"[sidenav]\" />\n </div>\n\n <div class=\"flex min-w-0 flex-1 flex-col\">\n @if (showMobileBar()) {\n <div\n class=\"flex shrink-0 items-center gap-2 border-b border-border px-3 py-2 dark:border-white/10\"\n [class.lg:hidden]=\"!layoutPreview()\"\n >\n <button\n type=\"button\"\n class=\"inline-flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-lg border border-border text-ink transition-colors hover:bg-background-welcome dark:border-white/15 dark:text-white dark:hover:bg-white/10\"\n [attr.aria-label]=\"mobileNavOpen() ? 'Close navigation' : 'Open navigation'\"\n [attr.aria-expanded]=\"mobileNavOpen()\"\n (click)=\"toggleMobileNav()\"\n >\n <africanies-icon name=\"align-justify\" [size]=\"18\" />\n </button>\n <img\n [src]=\"logoMiniUrl\"\n alt=\"African Import Export Solutions\"\n class=\"h-7 w-auto object-contain\"\n />\n </div>\n }\n\n <div\n class=\"sticky top-0 z-30 border-b border-border bg-white/90 backdrop-blur-md dark:border-white/10 dark:bg-ink-950/90\"\n >\n @if (hasCustomHeader()) {\n <ng-content select=\"[africaniesAppShellHeader]\" />\n } @else if (showHeaderChrome()) {\n <africanies-app-shell-header\n [userName]=\"userName()\"\n [greetingName]=\"greetingName()\"\n [userAvatarSrc]=\"userAvatarSrc()\"\n [userMenuItems]=\"userMenuItems()\"\n [notifications]=\"notifications()\"\n [notificationsTitle]=\"notificationsTitle()\"\n [showClock]=\"showClock()\"\n [showNotifications]=\"showNotifications()\"\n [clockDateFormat]=\"clockDateFormat()\"\n [clockFormat]=\"clockFormat()\"\n [density]=\"headerDensity()\"\n />\n }\n @if (showAccent()) {\n <div\n class=\"h-0.5 w-full transition-colors duration-300\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n }\n </div>\n\n <main [class]=\"mainClass()\">\n @if (showContentChrome()) {\n <africanies-app-shell-content-header\n [breadcrumbs]=\"breadcrumbs()\"\n [title]=\"headerTitle()\"\n [subtitle]=\"headerSubtitle()\"\n [url]=\"currentUrl()\"\n [catalogNav]=\"catalogNav()\"\n [showBack]=\"showContentBack()\"\n [backLink]=\"contentBackLink()\"\n [backLabel]=\"contentBackLabel()\"\n />\n }\n <ng-content />\n </main>\n </div>\n </div>", dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "component", type: AppShellContentHeaderComponent, selector: "africanies-app-shell-content-header", inputs: ["breadcrumbs", "title", "subtitle", "url", "catalogNav", "showBack", "backLink", "backLabel"] }, { kind: "component", type: AppShellHeaderComponent, selector: "africanies-app-shell-header", inputs: ["userName", "greetingName", "userAvatarSrc", "userMenuItems", "notifications", "notificationsTitle", "onNotificationMarkRead", "onNotificationMarkAllRead", "onNotificationLoadPage", "showClock", "showNotifications", "density", "clockDateFormat", "clockFormat"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
16996
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AppShellComponent, isStandalone: true, selector: "africanies-app-shell", inputs: { contentWidth: { classPropertyName: "contentWidth", publicName: "contentWidth", isSignal: true, isRequired: false, transformFunction: null }, showAccent: { classPropertyName: "showAccent", publicName: "showAccent", isSignal: true, isRequired: false, transformFunction: null }, showHeaderChrome: { classPropertyName: "showHeaderChrome", publicName: "showHeaderChrome", isSignal: true, isRequired: false, transformFunction: null }, layoutPreview: { classPropertyName: "layoutPreview", publicName: "layoutPreview", isSignal: true, isRequired: false, transformFunction: null }, headerTitle: { classPropertyName: "headerTitle", publicName: "headerTitle", isSignal: true, isRequired: false, transformFunction: null }, headerSubtitle: { classPropertyName: "headerSubtitle", publicName: "headerSubtitle", isSignal: true, isRequired: false, transformFunction: null }, breadcrumbs: { classPropertyName: "breadcrumbs", publicName: "breadcrumbs", isSignal: true, isRequired: false, transformFunction: null }, catalogNav: { classPropertyName: "catalogNav", publicName: "catalogNav", isSignal: true, isRequired: false, transformFunction: null }, showContentBack: { classPropertyName: "showContentBack", publicName: "showContentBack", isSignal: true, isRequired: false, transformFunction: null }, contentBackLink: { classPropertyName: "contentBackLink", publicName: "contentBackLink", isSignal: true, isRequired: false, transformFunction: null }, contentBackLabel: { classPropertyName: "contentBackLabel", publicName: "contentBackLabel", isSignal: true, isRequired: false, transformFunction: null }, userName: { classPropertyName: "userName", publicName: "userName", isSignal: true, isRequired: false, transformFunction: null }, greetingName: { classPropertyName: "greetingName", publicName: "greetingName", isSignal: true, isRequired: false, transformFunction: null }, userAvatarSrc: { classPropertyName: "userAvatarSrc", publicName: "userAvatarSrc", isSignal: true, isRequired: false, transformFunction: null }, userMenuItems: { classPropertyName: "userMenuItems", publicName: "userMenuItems", isSignal: true, isRequired: false, transformFunction: null }, notifications: { classPropertyName: "notifications", publicName: "notifications", isSignal: true, isRequired: false, transformFunction: null }, notificationsTitle: { classPropertyName: "notificationsTitle", publicName: "notificationsTitle", isSignal: true, isRequired: false, transformFunction: null }, showClock: { classPropertyName: "showClock", publicName: "showClock", isSignal: true, isRequired: false, transformFunction: null }, showNotifications: { classPropertyName: "showNotifications", publicName: "showNotifications", isSignal: true, isRequired: false, transformFunction: null }, clockDateFormat: { classPropertyName: "clockDateFormat", publicName: "clockDateFormat", isSignal: true, isRequired: false, transformFunction: null }, clockFormat: { classPropertyName: "clockFormat", publicName: "clockFormat", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClass()" } }, queries: [{ propertyName: "customHeader", first: true, predicate: AppShellHeaderSlotDirective, descendants: true, isSignal: true }], ngImport: i0, template: " <div [class]=\"shellRootClass()\">\n @if (mobileNavOpen()) {\n <button\n type=\"button\"\n class=\"absolute inset-0 z-40 cursor-pointer bg-ink/40\"\n [class.lg:hidden]=\"!layoutPreview()\"\n aria-label=\"Close navigation\"\n (click)=\"closeMobileNav()\"\n ></button>\n }\n\n <div [class]=\"sidenavHostClass()\">\n <ng-content select=\"[sidenav]\" />\n </div>\n\n <div class=\"flex min-w-0 flex-1 flex-col\">\n @if (showMobileBar()) {\n <div\n class=\"flex shrink-0 items-center gap-2 border-b border-border px-3 py-2 dark:border-white/10\"\n [class.lg:hidden]=\"!layoutPreview()\"\n >\n <button\n type=\"button\"\n class=\"inline-flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-lg border border-control text-ink transition-colors hover:bg-background-hover dark:border-white/15 dark:text-white dark:hover:bg-white/10\"\n [attr.aria-label]=\"mobileNavOpen() ? 'Close navigation' : 'Open navigation'\"\n [attr.aria-expanded]=\"mobileNavOpen()\"\n (click)=\"toggleMobileNav()\"\n >\n <africanies-icon name=\"align-justify\" [size]=\"18\" />\n </button>\n <img\n [src]=\"logoMiniUrl\"\n alt=\"African Import Export Solutions\"\n class=\"h-7 w-auto object-contain\"\n />\n </div>\n }\n\n <div\n class=\"sticky top-0 z-30 border-b border-border bg-surface/90 backdrop-blur-md dark:border-white/10 dark:bg-ink-950/90\"\n >\n @if (hasCustomHeader()) {\n <ng-content select=\"[africaniesAppShellHeader]\" />\n } @else if (showHeaderChrome()) {\n <africanies-app-shell-header\n [userName]=\"userName()\"\n [greetingName]=\"greetingName()\"\n [userAvatarSrc]=\"userAvatarSrc()\"\n [userMenuItems]=\"userMenuItems()\"\n [notifications]=\"notifications()\"\n [notificationsTitle]=\"notificationsTitle()\"\n [showClock]=\"showClock()\"\n [showNotifications]=\"showNotifications()\"\n [clockDateFormat]=\"clockDateFormat()\"\n [clockFormat]=\"clockFormat()\"\n [density]=\"headerDensity()\"\n />\n }\n @if (showAccent()) {\n <div\n class=\"h-0.5 w-full transition-colors duration-300\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n }\n </div>\n\n <main [class]=\"mainClass()\">\n @if (showContentChrome()) {\n <africanies-app-shell-content-header\n [breadcrumbs]=\"breadcrumbs()\"\n [title]=\"headerTitle()\"\n [subtitle]=\"headerSubtitle()\"\n [url]=\"currentUrl()\"\n [catalogNav]=\"catalogNav()\"\n [showBack]=\"showContentBack()\"\n [backLink]=\"contentBackLink()\"\n [backLabel]=\"contentBackLabel()\"\n />\n }\n <ng-content />\n </main>\n </div>\n </div>", dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "component", type: AppShellContentHeaderComponent, selector: "africanies-app-shell-content-header", inputs: ["breadcrumbs", "title", "subtitle", "url", "catalogNav", "showBack", "backLink", "backLabel"] }, { kind: "component", type: AppShellHeaderComponent, selector: "africanies-app-shell-header", inputs: ["userName", "greetingName", "userAvatarSrc", "userMenuItems", "notifications", "notificationsTitle", "onNotificationMarkRead", "onNotificationMarkAllRead", "onNotificationLoadPage", "showClock", "showNotifications", "density", "clockDateFormat", "clockFormat"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
16921
16997
  }
16922
16998
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AppShellComponent, decorators: [{
16923
16999
  type: Component,
16924
17000
  args: [{ selector: 'africanies-app-shell', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AfricaniesIconComponent, AppShellContentHeaderComponent, AppShellHeaderComponent], host: {
16925
17001
  '[class]': 'hostClass()',
16926
- }, template: " <div [class]=\"shellRootClass()\">\n @if (mobileNavOpen()) {\n <button\n type=\"button\"\n class=\"absolute inset-0 z-40 cursor-pointer bg-ink/40\"\n [class.lg:hidden]=\"!layoutPreview()\"\n aria-label=\"Close navigation\"\n (click)=\"closeMobileNav()\"\n ></button>\n }\n\n <div [class]=\"sidenavHostClass()\">\n <ng-content select=\"[sidenav]\" />\n </div>\n\n <div class=\"flex min-w-0 flex-1 flex-col\">\n @if (showMobileBar()) {\n <div\n class=\"flex shrink-0 items-center gap-2 border-b border-border px-3 py-2 dark:border-white/10\"\n [class.lg:hidden]=\"!layoutPreview()\"\n >\n <button\n type=\"button\"\n class=\"inline-flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-lg border border-border text-ink transition-colors hover:bg-background-welcome dark:border-white/15 dark:text-white dark:hover:bg-white/10\"\n [attr.aria-label]=\"mobileNavOpen() ? 'Close navigation' : 'Open navigation'\"\n [attr.aria-expanded]=\"mobileNavOpen()\"\n (click)=\"toggleMobileNav()\"\n >\n <africanies-icon name=\"align-justify\" [size]=\"18\" />\n </button>\n <img\n [src]=\"logoMiniUrl\"\n alt=\"African Import Export Solutions\"\n class=\"h-7 w-auto object-contain\"\n />\n </div>\n }\n\n <div\n class=\"sticky top-0 z-30 border-b border-border bg-white/90 backdrop-blur-md dark:border-white/10 dark:bg-ink-950/90\"\n >\n @if (hasCustomHeader()) {\n <ng-content select=\"[africaniesAppShellHeader]\" />\n } @else if (showHeaderChrome()) {\n <africanies-app-shell-header\n [userName]=\"userName()\"\n [greetingName]=\"greetingName()\"\n [userAvatarSrc]=\"userAvatarSrc()\"\n [userMenuItems]=\"userMenuItems()\"\n [notifications]=\"notifications()\"\n [notificationsTitle]=\"notificationsTitle()\"\n [showClock]=\"showClock()\"\n [showNotifications]=\"showNotifications()\"\n [clockDateFormat]=\"clockDateFormat()\"\n [clockFormat]=\"clockFormat()\"\n [density]=\"headerDensity()\"\n />\n }\n @if (showAccent()) {\n <div\n class=\"h-0.5 w-full transition-colors duration-300\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n }\n </div>\n\n <main [class]=\"mainClass()\">\n @if (showContentChrome()) {\n <africanies-app-shell-content-header\n [breadcrumbs]=\"breadcrumbs()\"\n [title]=\"headerTitle()\"\n [subtitle]=\"headerSubtitle()\"\n [url]=\"currentUrl()\"\n [catalogNav]=\"catalogNav()\"\n [showBack]=\"showContentBack()\"\n [backLink]=\"contentBackLink()\"\n [backLabel]=\"contentBackLabel()\"\n />\n }\n <ng-content />\n </main>\n </div>\n </div>" }]
17002
+ }, template: " <div [class]=\"shellRootClass()\">\n @if (mobileNavOpen()) {\n <button\n type=\"button\"\n class=\"absolute inset-0 z-40 cursor-pointer bg-ink/40\"\n [class.lg:hidden]=\"!layoutPreview()\"\n aria-label=\"Close navigation\"\n (click)=\"closeMobileNav()\"\n ></button>\n }\n\n <div [class]=\"sidenavHostClass()\">\n <ng-content select=\"[sidenav]\" />\n </div>\n\n <div class=\"flex min-w-0 flex-1 flex-col\">\n @if (showMobileBar()) {\n <div\n class=\"flex shrink-0 items-center gap-2 border-b border-border px-3 py-2 dark:border-white/10\"\n [class.lg:hidden]=\"!layoutPreview()\"\n >\n <button\n type=\"button\"\n class=\"inline-flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-lg border border-control text-ink transition-colors hover:bg-background-hover dark:border-white/15 dark:text-white dark:hover:bg-white/10\"\n [attr.aria-label]=\"mobileNavOpen() ? 'Close navigation' : 'Open navigation'\"\n [attr.aria-expanded]=\"mobileNavOpen()\"\n (click)=\"toggleMobileNav()\"\n >\n <africanies-icon name=\"align-justify\" [size]=\"18\" />\n </button>\n <img\n [src]=\"logoMiniUrl\"\n alt=\"African Import Export Solutions\"\n class=\"h-7 w-auto object-contain\"\n />\n </div>\n }\n\n <div\n class=\"sticky top-0 z-30 border-b border-border bg-surface/90 backdrop-blur-md dark:border-white/10 dark:bg-ink-950/90\"\n >\n @if (hasCustomHeader()) {\n <ng-content select=\"[africaniesAppShellHeader]\" />\n } @else if (showHeaderChrome()) {\n <africanies-app-shell-header\n [userName]=\"userName()\"\n [greetingName]=\"greetingName()\"\n [userAvatarSrc]=\"userAvatarSrc()\"\n [userMenuItems]=\"userMenuItems()\"\n [notifications]=\"notifications()\"\n [notificationsTitle]=\"notificationsTitle()\"\n [showClock]=\"showClock()\"\n [showNotifications]=\"showNotifications()\"\n [clockDateFormat]=\"clockDateFormat()\"\n [clockFormat]=\"clockFormat()\"\n [density]=\"headerDensity()\"\n />\n }\n @if (showAccent()) {\n <div\n class=\"h-0.5 w-full transition-colors duration-300\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n }\n </div>\n\n <main [class]=\"mainClass()\">\n @if (showContentChrome()) {\n <africanies-app-shell-content-header\n [breadcrumbs]=\"breadcrumbs()\"\n [title]=\"headerTitle()\"\n [subtitle]=\"headerSubtitle()\"\n [url]=\"currentUrl()\"\n [catalogNav]=\"catalogNav()\"\n [showBack]=\"showContentBack()\"\n [backLink]=\"contentBackLink()\"\n [backLabel]=\"contentBackLabel()\"\n />\n }\n <ng-content />\n </main>\n </div>\n </div>" }]
16927
17003
  }], propDecorators: { customHeader: [{ type: i0.ContentChild, args: [i0.forwardRef(() => AppShellHeaderSlotDirective), { isSignal: true }] }], contentWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentWidth", required: false }] }], showAccent: [{ type: i0.Input, args: [{ isSignal: true, alias: "showAccent", required: false }] }], showHeaderChrome: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHeaderChrome", required: false }] }], layoutPreview: [{ type: i0.Input, args: [{ isSignal: true, alias: "layoutPreview", required: false }] }], headerTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerTitle", required: false }] }], headerSubtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerSubtitle", required: false }] }], breadcrumbs: [{ type: i0.Input, args: [{ isSignal: true, alias: "breadcrumbs", required: false }] }], catalogNav: [{ type: i0.Input, args: [{ isSignal: true, alias: "catalogNav", required: false }] }], showContentBack: [{ type: i0.Input, args: [{ isSignal: true, alias: "showContentBack", required: false }] }], contentBackLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentBackLink", required: false }] }], contentBackLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentBackLabel", required: false }] }], userName: [{ type: i0.Input, args: [{ isSignal: true, alias: "userName", required: false }] }], greetingName: [{ type: i0.Input, args: [{ isSignal: true, alias: "greetingName", required: false }] }], userAvatarSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "userAvatarSrc", required: false }] }], userMenuItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "userMenuItems", required: false }] }], notifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "notifications", required: false }] }], notificationsTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "notificationsTitle", required: false }] }], showClock: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClock", required: false }] }], showNotifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "showNotifications", required: false }] }], clockDateFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "clockDateFormat", required: false }] }], clockFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "clockFormat", required: false }] }] } });
16928
17004
 
16929
17005
  /**
@@ -17051,7 +17127,7 @@ class ImageComponent {
17051
17127
 
17052
17128
  @if (showPlaceholder()) {
17053
17129
  <div
17054
- class="absolute inset-0 flex items-center justify-center bg-neutral-200 text-neutral-500 dark:bg-white/10 dark:text-neutral-400"
17130
+ class="absolute inset-0 flex items-center justify-center bg-neutral-200 text-neutral-600 dark:bg-white/10 dark:text-neutral-400"
17055
17131
  [class.animate-pulse]="pending()"
17056
17132
  aria-hidden="true"
17057
17133
  >
@@ -17107,7 +17183,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17107
17183
 
17108
17184
  @if (showPlaceholder()) {
17109
17185
  <div
17110
- class="absolute inset-0 flex items-center justify-center bg-neutral-200 text-neutral-500 dark:bg-white/10 dark:text-neutral-400"
17186
+ class="absolute inset-0 flex items-center justify-center bg-neutral-200 text-neutral-600 dark:bg-white/10 dark:text-neutral-400"
17111
17187
  [class.animate-pulse]="pending()"
17112
17188
  aria-hidden="true"
17113
17189
  >
@@ -17461,7 +17537,7 @@ class PaginationComponent {
17461
17537
  @for (item of pageItems(); track trackPageItem(item, $index)) {
17462
17538
  @if (item === 'ellipsis') {
17463
17539
  <span
17464
- class="inline-flex min-w-8 items-center justify-center text-body-sm text-neutral-500 dark:text-neutral-400"
17540
+ class="inline-flex min-w-8 items-center justify-center text-body-sm text-neutral-600 dark:text-neutral-400"
17465
17541
  aria-hidden="true"
17466
17542
  >
17467
17543
 
@@ -17562,7 +17638,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17562
17638
  @for (item of pageItems(); track trackPageItem(item, $index)) {
17563
17639
  @if (item === 'ellipsis') {
17564
17640
  <span
17565
- class="inline-flex min-w-8 items-center justify-center text-body-sm text-neutral-500 dark:text-neutral-400"
17641
+ class="inline-flex min-w-8 items-center justify-center text-body-sm text-neutral-600 dark:text-neutral-400"
17566
17642
  aria-hidden="true"
17567
17643
  >
17568
17644
 
@@ -17612,11 +17688,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17612
17688
  * Hidden while the body shows empty or error — those states expose Retry instead.
17613
17689
  * While {@link refreshing} is true the rows stay on screen — the refresh icon
17614
17690
  * spins (do not swap to a blocking loader). Use {@link loading} when the page
17615
- * of data is changing (pagination / size): rows stay on screen and the built-in
17616
- * pager shows a circular spinner beside the page controls; the body loader
17617
- * appears only when there are no rows yet (first load) or the host cleared
17618
- * rows for a shipping-mode switch (STN SFN). Toolbar and column headers stay
17619
- * mounted.
17691
+ * of data is changing (pagination / size): rows stay on screen, a keep-rows
17692
+ * overlay covers the grid, and the built-in pager shows a circular spinner
17693
+ * beside the page controls; the body loader appears only when there are no
17694
+ * rows yet (first load) or the host cleared rows for a shipping-mode switch
17695
+ * (STN ↔ SFN). Toolbar and column headers stay mounted.
17620
17696
  * top-right **Filters** then **Export** (`showFilter` / `showExport`). The table
17621
17697
  * does not own fetch, filter, or export logic — the host handles those events.
17622
17698
  *
@@ -17675,6 +17751,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17675
17751
  * to the left when rows are expandable. Override with
17676
17752
  * {@link TableColumn.sticky}. Sticky columns use a slightly thicker bottom
17677
17753
  * border (and opaque fill) so dividers stay visible while scrolling.
17754
+ *
17755
+ * **Sticky header (desktop):** when {@link stickyHeader} is true (default),
17756
+ * large screens scroll the grid inside a capped viewport and keep `th` pinned
17757
+ * to the top of that scrollport. Mobile keeps horizontal-only overflow so
17758
+ * page scroll is unchanged.
17678
17759
  */
17679
17760
  class TableComponent {
17680
17761
  modeColor = inject(ModeColorService);
@@ -17693,6 +17774,12 @@ class TableComponent {
17693
17774
  /** Column definitions (header chrome + keys). */
17694
17775
  columns = input.required(/* @ts-ignore */
17695
17776
  ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
17777
+ /**
17778
+ * When true (default), pin column headers while scrolling the grid on
17779
+ * large screens (`lg+`). Uses a capped scrollport so sticky works with
17780
+ * horizontal overflow. Set false to keep the previous unconstrained layout.
17781
+ */
17782
+ stickyHeader = input(true, { ...(ngDevMode ? { debugName: "stickyHeader" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
17696
17783
  /**
17697
17784
  * Row records to render. Empty arrays show {@link EmptyStateComponent} in
17698
17785
  * the body unless {@link loading} or {@link error} is set. Missing `data`
@@ -17723,9 +17810,9 @@ class TableComponent {
17723
17810
  refreshing = input(false, { ...(ngDevMode ? { debugName: "refreshing" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
17724
17811
  /**
17725
17812
  * Page of data is loading (first load, shipping-mode switch, pagination,
17726
- * page size). When rows are already on screen, the pager shows a spinner
17727
- * and rows stay visible; when there are no rows (first load or the host
17728
- * dropped STN/SFN rows after a mode switch), the body shows
17813
+ * page size). When rows are already on screen, a keep-rows overlay covers
17814
+ * the grid and the pager shows a spinner; when there are no rows (first
17815
+ * load or the host dropped STN/SFN rows after a mode switch), the body shows
17729
17816
  * {@link LoadingStateComponent}. Distinct from {@link refreshing}.
17730
17817
  */
17731
17818
  loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
@@ -17974,7 +18061,18 @@ class TableComponent {
17974
18061
  'border-border whitespace-normal wrap-break-word px-[1.125rem] py-4 font-medium text-neutral-600 dark:border-white/10 dark:text-neutral-400 ' +
17975
18062
  this.headerSoftClass() +
17976
18063
  ' ' +
17977
- this.stickySurfaceClass(col, 'head'));
18064
+ this.stickySurfaceClass(col, 'head') +
18065
+ (sticky ? '' : this.headerStickClass()));
18066
+ }
18067
+ /**
18068
+ * Vertical pin for scrolling header cells on desktop. Pinned left/right
18069
+ * headers get `lg:top-0` from {@link stickyPinClass} instead.
18070
+ */
18071
+ headerStickClass() {
18072
+ if (!this.stickyHeader()) {
18073
+ return '';
18074
+ }
18075
+ return ' lg:sticky lg:top-0 lg:z-[3] print:static';
17978
18076
  }
17979
18077
  /**
17980
18078
  * Body cell classes, including sticky pin when set.
@@ -18013,9 +18111,13 @@ class TableComponent {
18013
18111
  const side = edge === 'right' ? 'sticky right-0 z-[1]' : 'sticky left-0 z-[1]';
18014
18112
  if (surface === 'head') {
18015
18113
  // Fill comes from {@link headerSoftClass} on the cell — do not re-tint.
18016
- return `${side} z-[2]`;
18114
+ // Corner pins also stick vertically on desktop when stickyHeader is on.
18115
+ const vertical = this.stickyHeader()
18116
+ ? ' z-[2] lg:top-0 lg:z-[4] print:static'
18117
+ : ' z-[2]';
18118
+ return `${side}${vertical}`;
18017
18119
  }
18018
- const fill = 'bg-white dark:bg-ink-surface';
18120
+ const fill = 'bg-surface dark:bg-ink-surface';
18019
18121
  return `${side} ${fill}`;
18020
18122
  }
18021
18123
  /**
@@ -18121,7 +18223,7 @@ class TableComponent {
18121
18223
  this.sortChange.emit({ key, direction });
18122
18224
  }
18123
18225
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: TableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18124
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: TableComponent, isStandalone: true, selector: "africanies-table", inputs: { expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, rowTrackBy: { classPropertyName: "rowTrackBy", publicName: "rowTrackBy", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, showRefresh: { classPropertyName: "showRefresh", publicName: "showRefresh", isSignal: true, isRequired: false, transformFunction: null }, refreshLabel: { classPropertyName: "refreshLabel", publicName: "refreshLabel", isSignal: true, isRequired: false, transformFunction: null }, refreshing: { classPropertyName: "refreshing", publicName: "refreshing", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingLabel: { classPropertyName: "loadingLabel", publicName: "loadingLabel", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, showFilter: { classPropertyName: "showFilter", publicName: "showFilter", isSignal: true, isRequired: false, transformFunction: null }, filterLabel: { classPropertyName: "filterLabel", publicName: "filterLabel", isSignal: true, isRequired: false, transformFunction: null }, filterCount: { classPropertyName: "filterCount", publicName: "filterCount", isSignal: true, isRequired: false, transformFunction: null }, showExport: { classPropertyName: "showExport", publicName: "showExport", isSignal: true, isRequired: false, transformFunction: null }, exportLabel: { classPropertyName: "exportLabel", publicName: "exportLabel", isSignal: true, isRequired: false, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowExpandChange: "rowExpandChange", sortChange: "sortChange", refreshClick: "refreshClick", filterClick: "filterClick", exportClick: "exportClick", pageChange: "pageChange", sizeChange: "sizeChange" }, host: { classAttribute: "block w-full min-w-0" }, queries: [{ propertyName: "cellDefs", predicate: CellDefDirective, isSignal: true }, { propertyName: "headerCellDefs", predicate: HeaderCellDefDirective, isSignal: true }, { propertyName: "rowDetailDefs", predicate: RowDetailDefDirective, isSignal: true }], ngImport: i0, template: `
18226
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: TableComponent, isStandalone: true, selector: "africanies-table", inputs: { expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, rowTrackBy: { classPropertyName: "rowTrackBy", publicName: "rowTrackBy", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, stickyHeader: { classPropertyName: "stickyHeader", publicName: "stickyHeader", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, showRefresh: { classPropertyName: "showRefresh", publicName: "showRefresh", isSignal: true, isRequired: false, transformFunction: null }, refreshLabel: { classPropertyName: "refreshLabel", publicName: "refreshLabel", isSignal: true, isRequired: false, transformFunction: null }, refreshing: { classPropertyName: "refreshing", publicName: "refreshing", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingLabel: { classPropertyName: "loadingLabel", publicName: "loadingLabel", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, showFilter: { classPropertyName: "showFilter", publicName: "showFilter", isSignal: true, isRequired: false, transformFunction: null }, filterLabel: { classPropertyName: "filterLabel", publicName: "filterLabel", isSignal: true, isRequired: false, transformFunction: null }, filterCount: { classPropertyName: "filterCount", publicName: "filterCount", isSignal: true, isRequired: false, transformFunction: null }, showExport: { classPropertyName: "showExport", publicName: "showExport", isSignal: true, isRequired: false, transformFunction: null }, exportLabel: { classPropertyName: "exportLabel", publicName: "exportLabel", isSignal: true, isRequired: false, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowExpandChange: "rowExpandChange", sortChange: "sortChange", refreshClick: "refreshClick", filterClick: "filterClick", exportClick: "exportClick", pageChange: "pageChange", sizeChange: "sizeChange" }, host: { classAttribute: "block w-full min-w-0" }, queries: [{ propertyName: "cellDefs", predicate: CellDefDirective, isSignal: true }, { propertyName: "headerCellDefs", predicate: HeaderCellDefDirective, isSignal: true }, { propertyName: "rowDetailDefs", predicate: RowDetailDefDirective, isSignal: true }], ngImport: i0, template: `
18125
18227
  <div
18126
18228
  class="flex w-full min-w-0 flex-col gap-3"
18127
18229
  [attr.aria-busy]="loading() || refreshing() || null"
@@ -18187,19 +18289,19 @@ class TableComponent {
18187
18289
  }
18188
18290
 
18189
18291
  @if (staleError(); as staleMessage) {
18190
- <div class="flex justify-end">
18191
- <africanies-error-indicator
18192
- class="max-w-[min(100%,20rem)]"
18193
- [error]="staleMessage"
18194
- retryText="Refresh"
18195
- [refreshing]="refreshing()"
18196
- (retry)="refreshClick.emit()"
18197
- />
18198
- </div>
18292
+ <africanies-error-indicator
18293
+ class="w-full"
18294
+ [error]="staleMessage"
18295
+ retryText="Refresh"
18296
+ [refreshing]="refreshing()"
18297
+ (retry)="refreshClick.emit()"
18298
+ />
18199
18299
  }
18200
18300
 
18201
18301
  <div
18202
- class="min-w-0 w-full overflow-x-auto rounded-md border border-border bg-white dark:border-white/10 dark:bg-ink-surface"
18302
+ class="relative min-w-0 w-full overflow-x-auto rounded-panel border border-border bg-surface dark:border-white/10 dark:bg-ink-surface"
18303
+ [class.lg:max-h-[min(70dvh,calc(100dvh-12rem))]]="stickyHeader()"
18304
+ [class.lg:overflow-auto]="stickyHeader()"
18203
18305
  >
18204
18306
  <table
18205
18307
  class="w-max min-w-full table-auto border-separate border-spacing-0 bg-inherit text-left text-body text-ink dark:text-white"
@@ -18214,7 +18316,7 @@ class TableComponent {
18214
18316
  @if (someRowsExpanded()) {
18215
18317
  <button
18216
18318
  type="button"
18217
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-white hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18319
+ class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18218
18320
  aria-label="Collapse all"
18219
18321
  (click)="collapseAllRows()"
18220
18322
  >
@@ -18223,7 +18325,7 @@ class TableComponent {
18223
18325
  } @else {
18224
18326
  <button
18225
18327
  type="button"
18226
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-white hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink disabled:cursor-not-allowed disabled:opacity-40 dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18328
+ class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus disabled:cursor-not-allowed disabled:opacity-40 dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18227
18329
  aria-label="Expand all"
18228
18330
  [disabled]="rowList().length === 0"
18229
18331
  (click)="expandAllRows()"
@@ -18237,7 +18339,8 @@ class TableComponent {
18237
18339
  <th
18238
18340
  scope="col"
18239
18341
  [class]="headerCellClass(col)"
18240
- [style.width]="col.width ?? null"
18342
+ [style.width]="col.width ?? col.maxWidth ?? null"
18343
+ [style.max-width]="col.maxWidth ?? null"
18241
18344
  >
18242
18345
  @if (headerTemplateFor(col.key); as headerTpl) {
18243
18346
  <ng-container
@@ -18246,7 +18349,7 @@ class TableComponent {
18246
18349
  } @else if (col.sortable) {
18247
18350
  <button
18248
18351
  type="button"
18249
- class="inline-flex cursor-pointer items-center gap-1 rounded-sm font-medium text-neutral-600 hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:text-white"
18352
+ class="inline-flex cursor-pointer items-center gap-1 rounded-sm font-medium text-neutral-600 hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:text-white"
18250
18353
  (click)="onSortHeaderClick(col.key)"
18251
18354
  >
18252
18355
  <span>{{ col.header }}</span>
@@ -18302,7 +18405,7 @@ class TableComponent {
18302
18405
  <td [class]="expandBodyClass(i)">
18303
18406
  <button
18304
18407
  type="button"
18305
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18408
+ class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18306
18409
  [attr.aria-label]="
18307
18410
  isRowExpanded(row, i)
18308
18411
  ? 'Collapse row details'
@@ -18321,7 +18424,8 @@ class TableComponent {
18321
18424
  @for (col of columns(); track col.key) {
18322
18425
  <td
18323
18426
  [class]="bodyCellClass(col, i)"
18324
- [style.width]="col.width ?? null"
18427
+ [style.width]="col.width ?? col.maxWidth ?? null"
18428
+ [style.max-width]="col.maxWidth ?? null"
18325
18429
  >
18326
18430
  @if (templateFor(col.key); as tpl) {
18327
18431
  <ng-container
@@ -18351,7 +18455,7 @@ class TableComponent {
18351
18455
  [class.pointer-events-none]="!isRowExpanded(row, i)"
18352
18456
  >
18353
18457
  <div
18354
- class="min-h-0 overflow-hidden border-t border-border/70 bg-neutral-50 dark:border-white/10 dark:bg-white/[0.05]"
18458
+ class="min-h-0 overflow-hidden border-t border-border-strong bg-surface-sunken dark:border-white/10 dark:bg-white/[0.05]"
18355
18459
  >
18356
18460
  <dl
18357
18461
  class="m-0 grid gap-x-6 gap-y-3 px-[1.125rem] py-4 transition-opacity duration-200 ease-out motion-reduce:transition-none sm:grid-cols-2 lg:grid-cols-3"
@@ -18361,7 +18465,7 @@ class TableComponent {
18361
18465
  @for (detail of rowDetailDefs(); track detail.label()) {
18362
18466
  <div class="min-w-0">
18363
18467
  <dt
18364
- class="m-0 text-caption font-medium text-neutral-500 dark:text-neutral-400"
18468
+ class="m-0 text-caption font-medium text-neutral-600 dark:text-neutral-400"
18365
18469
  >
18366
18470
  {{ detail.label() }}
18367
18471
  </dt>
@@ -18386,6 +18490,18 @@ class TableComponent {
18386
18490
  }
18387
18491
  </tbody>
18388
18492
  </table>
18493
+ @if (paginationLoading()) {
18494
+ <div
18495
+ class="pointer-events-none absolute inset-0 z-[5] flex items-start justify-center bg-surface/70 px-4 pt-16 dark:bg-ink-950/70"
18496
+ data-testid="africanies-table-keep-rows-loading"
18497
+ aria-hidden="true"
18498
+ >
18499
+ <africanies-loading-state
18500
+ mode="inline"
18501
+ [message]="loadingLabel()"
18502
+ />
18503
+ </div>
18504
+ }
18389
18505
  </div>
18390
18506
 
18391
18507
  @if (meta(); as pager) {
@@ -18398,7 +18514,7 @@ class TableComponent {
18398
18514
  />
18399
18515
  }
18400
18516
  </div>
18401
- `, isInline: true, styles: [":host tr.africanies-table-row:hover>td{background-color:#eef2f6}:host-context(.dark) tr.africanies-table-row:hover>td{background-color:#272729}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ButtonComponent, selector: "button[africanies-button], a[africanies-button]", inputs: ["variant", "size", "disabled", "loading"] }, { kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "component", type: EmptyStateComponent, selector: "africanies-empty-state", inputs: ["message", "refreshing"], outputs: ["retry"] }, { kind: "component", type: ErrorIndicatorComponent, selector: "africanies-error-indicator", inputs: ["error", "retryText", "refreshing", "refreshingText", "disabled"], outputs: ["retry"] }, { kind: "component", type: ErrorStateComponent, selector: "africanies-error-state", inputs: ["message", "refreshing"], outputs: ["retry"] }, { kind: "component", type: LoadingStateComponent, selector: "africanies-loading-state", inputs: ["message", "mode"] }, { kind: "component", type: PaginationComponent, selector: "africanies-pagination", inputs: ["meta", "disabled", "loading", "pageSizes"], outputs: ["pageChange", "sizeChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
18517
+ `, isInline: true, styles: [":host tr.africanies-table-row:hover>td{background-color:#e9eef5}:host-context(.dark) tr.africanies-table-row:hover>td{background-color:#272729}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ButtonComponent, selector: "button[africanies-button], a[africanies-button]", inputs: ["variant", "size", "disabled", "loading"] }, { kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "component", type: EmptyStateComponent, selector: "africanies-empty-state", inputs: ["message", "refreshing"], outputs: ["retry"] }, { kind: "component", type: ErrorIndicatorComponent, selector: "africanies-error-indicator", inputs: ["error", "retryText", "refreshing", "refreshingText", "disabled"], outputs: ["retry"] }, { kind: "component", type: ErrorStateComponent, selector: "africanies-error-state", inputs: ["message", "refreshing"], outputs: ["retry"] }, { kind: "component", type: LoadingStateComponent, selector: "africanies-loading-state", inputs: ["message", "mode"] }, { kind: "component", type: PaginationComponent, selector: "africanies-pagination", inputs: ["meta", "disabled", "loading", "pageSizes"], outputs: ["pageChange", "sizeChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
18402
18518
  }
18403
18519
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: TableComponent, decorators: [{
18404
18520
  type: Component,
@@ -18479,19 +18595,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18479
18595
  }
18480
18596
 
18481
18597
  @if (staleError(); as staleMessage) {
18482
- <div class="flex justify-end">
18483
- <africanies-error-indicator
18484
- class="max-w-[min(100%,20rem)]"
18485
- [error]="staleMessage"
18486
- retryText="Refresh"
18487
- [refreshing]="refreshing()"
18488
- (retry)="refreshClick.emit()"
18489
- />
18490
- </div>
18598
+ <africanies-error-indicator
18599
+ class="w-full"
18600
+ [error]="staleMessage"
18601
+ retryText="Refresh"
18602
+ [refreshing]="refreshing()"
18603
+ (retry)="refreshClick.emit()"
18604
+ />
18491
18605
  }
18492
18606
 
18493
18607
  <div
18494
- class="min-w-0 w-full overflow-x-auto rounded-md border border-border bg-white dark:border-white/10 dark:bg-ink-surface"
18608
+ class="relative min-w-0 w-full overflow-x-auto rounded-panel border border-border bg-surface dark:border-white/10 dark:bg-ink-surface"
18609
+ [class.lg:max-h-[min(70dvh,calc(100dvh-12rem))]]="stickyHeader()"
18610
+ [class.lg:overflow-auto]="stickyHeader()"
18495
18611
  >
18496
18612
  <table
18497
18613
  class="w-max min-w-full table-auto border-separate border-spacing-0 bg-inherit text-left text-body text-ink dark:text-white"
@@ -18506,7 +18622,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18506
18622
  @if (someRowsExpanded()) {
18507
18623
  <button
18508
18624
  type="button"
18509
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-white hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18625
+ class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18510
18626
  aria-label="Collapse all"
18511
18627
  (click)="collapseAllRows()"
18512
18628
  >
@@ -18515,7 +18631,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18515
18631
  } @else {
18516
18632
  <button
18517
18633
  type="button"
18518
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-white hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink disabled:cursor-not-allowed disabled:opacity-40 dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18634
+ class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus disabled:cursor-not-allowed disabled:opacity-40 dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18519
18635
  aria-label="Expand all"
18520
18636
  [disabled]="rowList().length === 0"
18521
18637
  (click)="expandAllRows()"
@@ -18529,7 +18645,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18529
18645
  <th
18530
18646
  scope="col"
18531
18647
  [class]="headerCellClass(col)"
18532
- [style.width]="col.width ?? null"
18648
+ [style.width]="col.width ?? col.maxWidth ?? null"
18649
+ [style.max-width]="col.maxWidth ?? null"
18533
18650
  >
18534
18651
  @if (headerTemplateFor(col.key); as headerTpl) {
18535
18652
  <ng-container
@@ -18538,7 +18655,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18538
18655
  } @else if (col.sortable) {
18539
18656
  <button
18540
18657
  type="button"
18541
- class="inline-flex cursor-pointer items-center gap-1 rounded-sm font-medium text-neutral-600 hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:text-white"
18658
+ class="inline-flex cursor-pointer items-center gap-1 rounded-sm font-medium text-neutral-600 hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:text-white"
18542
18659
  (click)="onSortHeaderClick(col.key)"
18543
18660
  >
18544
18661
  <span>{{ col.header }}</span>
@@ -18594,7 +18711,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18594
18711
  <td [class]="expandBodyClass(i)">
18595
18712
  <button
18596
18713
  type="button"
18597
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18714
+ class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18598
18715
  [attr.aria-label]="
18599
18716
  isRowExpanded(row, i)
18600
18717
  ? 'Collapse row details'
@@ -18613,7 +18730,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18613
18730
  @for (col of columns(); track col.key) {
18614
18731
  <td
18615
18732
  [class]="bodyCellClass(col, i)"
18616
- [style.width]="col.width ?? null"
18733
+ [style.width]="col.width ?? col.maxWidth ?? null"
18734
+ [style.max-width]="col.maxWidth ?? null"
18617
18735
  >
18618
18736
  @if (templateFor(col.key); as tpl) {
18619
18737
  <ng-container
@@ -18643,7 +18761,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18643
18761
  [class.pointer-events-none]="!isRowExpanded(row, i)"
18644
18762
  >
18645
18763
  <div
18646
- class="min-h-0 overflow-hidden border-t border-border/70 bg-neutral-50 dark:border-white/10 dark:bg-white/[0.05]"
18764
+ class="min-h-0 overflow-hidden border-t border-border-strong bg-surface-sunken dark:border-white/10 dark:bg-white/[0.05]"
18647
18765
  >
18648
18766
  <dl
18649
18767
  class="m-0 grid gap-x-6 gap-y-3 px-[1.125rem] py-4 transition-opacity duration-200 ease-out motion-reduce:transition-none sm:grid-cols-2 lg:grid-cols-3"
@@ -18653,7 +18771,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18653
18771
  @for (detail of rowDetailDefs(); track detail.label()) {
18654
18772
  <div class="min-w-0">
18655
18773
  <dt
18656
- class="m-0 text-caption font-medium text-neutral-500 dark:text-neutral-400"
18774
+ class="m-0 text-caption font-medium text-neutral-600 dark:text-neutral-400"
18657
18775
  >
18658
18776
  {{ detail.label() }}
18659
18777
  </dt>
@@ -18678,6 +18796,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18678
18796
  }
18679
18797
  </tbody>
18680
18798
  </table>
18799
+ @if (paginationLoading()) {
18800
+ <div
18801
+ class="pointer-events-none absolute inset-0 z-[5] flex items-start justify-center bg-surface/70 px-4 pt-16 dark:bg-ink-950/70"
18802
+ data-testid="africanies-table-keep-rows-loading"
18803
+ aria-hidden="true"
18804
+ >
18805
+ <africanies-loading-state
18806
+ mode="inline"
18807
+ [message]="loadingLabel()"
18808
+ />
18809
+ </div>
18810
+ }
18681
18811
  </div>
18682
18812
 
18683
18813
  @if (meta(); as pager) {
@@ -18690,8 +18820,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18690
18820
  />
18691
18821
  }
18692
18822
  </div>
18693
- `, styles: [":host tr.africanies-table-row:hover>td{background-color:#eef2f6}:host-context(.dark) tr.africanies-table-row:hover>td{background-color:#272729}\n"] }]
18694
- }], propDecorators: { expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], rowTrackBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowTrackBy", required: false }] }], rowExpandChange: [{ type: i0.Output, args: ["rowExpandChange"] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }], showRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRefresh", required: false }] }], refreshLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "refreshLabel", required: false }] }], refreshing: [{ type: i0.Input, args: [{ isSignal: true, alias: "refreshing", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], loadingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingLabel", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], showFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFilter", required: false }] }], filterLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterLabel", required: false }] }], filterCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterCount", required: false }] }], showExport: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExport", required: false }] }], exportLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportLabel", required: false }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], refreshClick: [{ type: i0.Output, args: ["refreshClick"] }], filterClick: [{ type: i0.Output, args: ["filterClick"] }], exportClick: [{ type: i0.Output, args: ["exportClick"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], sizeChange: [{ type: i0.Output, args: ["sizeChange"] }], cellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => CellDefDirective), { isSignal: true }] }], headerCellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => HeaderCellDefDirective), { isSignal: true }] }], rowDetailDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => RowDetailDefDirective), { isSignal: true }] }] } });
18823
+ `, styles: [":host tr.africanies-table-row:hover>td{background-color:#e9eef5}:host-context(.dark) tr.africanies-table-row:hover>td{background-color:#272729}\n"] }]
18824
+ }], propDecorators: { expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], rowTrackBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowTrackBy", required: false }] }], rowExpandChange: [{ type: i0.Output, args: ["rowExpandChange"] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], stickyHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "stickyHeader", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }], showRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRefresh", required: false }] }], refreshLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "refreshLabel", required: false }] }], refreshing: [{ type: i0.Input, args: [{ isSignal: true, alias: "refreshing", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], loadingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingLabel", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], showFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFilter", required: false }] }], filterLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterLabel", required: false }] }], filterCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterCount", required: false }] }], showExport: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExport", required: false }] }], exportLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportLabel", required: false }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], refreshClick: [{ type: i0.Output, args: ["refreshClick"] }], filterClick: [{ type: i0.Output, args: ["filterClick"] }], exportClick: [{ type: i0.Output, args: ["exportClick"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], sizeChange: [{ type: i0.Output, args: ["sizeChange"] }], cellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => CellDefDirective), { isSignal: true }] }], headerCellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => HeaderCellDefDirective), { isSignal: true }] }], rowDetailDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => RowDetailDefDirective), { isSignal: true }] }] } });
18695
18825
 
18696
18826
  /**
18697
18827
  * Presentational table with template-based cells.
@@ -18837,7 +18967,7 @@ class StepperComponent {
18837
18967
  if (past) {
18838
18968
  return 'cursor-pointer text-ink hover:bg-background-welcome dark:text-white dark:hover:bg-white/10';
18839
18969
  }
18840
- return 'opacity-40 text-neutral-500 dark:text-neutral-400';
18970
+ return 'opacity-40 text-neutral-600 dark:text-neutral-400';
18841
18971
  }
18842
18972
  stepBadgeClass(index) {
18843
18973
  const base = 'inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full border text-body-sm font-medium';
@@ -18845,7 +18975,7 @@ class StepperComponent {
18845
18975
  const c = this.modeColor.classes();
18846
18976
  return `${base} ${c.border} ${c.bg} text-white`;
18847
18977
  }
18848
- return `${base} border-border bg-white text-neutral-500 dark:border-white/20 dark:bg-ink-950 dark:text-neutral-400`;
18978
+ return `${base} border-control bg-surface text-neutral-600 dark:border-white/20 dark:bg-ink-950 dark:text-neutral-400`;
18849
18979
  }
18850
18980
  canActivate(index) {
18851
18981
  if (index === this.activeIndex()) {
@@ -18870,7 +19000,7 @@ class StepperComponent {
18870
19000
  <div class="flex flex-col gap-5 text-ink dark:text-white">
18871
19001
  <div class="flex flex-col gap-2">
18872
19002
  <p
18873
- class="m-0 text-caption font-medium uppercase tracking-wide text-neutral-500 dark:text-neutral-400"
19003
+ class="m-0 text-caption font-medium uppercase tracking-wide text-neutral-600 dark:text-neutral-400"
18874
19004
  >
18875
19005
  Step {{ activeIndex() + 1 }} of {{ steps().length }}
18876
19006
  <span
@@ -18899,7 +19029,7 @@ class StepperComponent {
18899
19029
  <li class="flex min-w-0 shrink-0 items-center gap-1">
18900
19030
  <button
18901
19031
  type="button"
18902
- class="inline-flex max-w-38 items-center gap-2 rounded-lg px-2 py-1.5 text-left transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink disabled:cursor-not-allowed"
19032
+ class="inline-flex max-w-38 items-center gap-2 rounded-lg px-2 py-1.5 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ink disabled:cursor-not-allowed"
18903
19033
  [class]="headerButtonClass(i)"
18904
19034
  [attr.aria-current]="i === activeIndex() ? 'step' : null"
18905
19035
  [disabled]="!canActivate(i)"
@@ -18938,7 +19068,7 @@ class StepperComponent {
18938
19068
  }
18939
19069
  </div>
18940
19070
  </div>
18941
- `, isInline: true, styles: [".africanies-step-rail{scrollbar-width:thin}.africanies-step-body{min-width:0;overflow-x:clip;padding-inline:2px;margin-inline:-2px}.africanies-step-pane{min-width:0;will-change:opacity,transform}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
19071
+ `, isInline: true, styles: [".africanies-step-rail{scrollbar-width:thin}.africanies-step-body{min-width:0;overflow-x:clip;padding-inline:4px;margin-inline:-4px}.africanies-step-pane{min-width:0;will-change:opacity,transform}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
18942
19072
  }
18943
19073
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: StepperComponent, decorators: [{
18944
19074
  type: Component,
@@ -18946,7 +19076,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18946
19076
  <div class="flex flex-col gap-5 text-ink dark:text-white">
18947
19077
  <div class="flex flex-col gap-2">
18948
19078
  <p
18949
- class="m-0 text-caption font-medium uppercase tracking-wide text-neutral-500 dark:text-neutral-400"
19079
+ class="m-0 text-caption font-medium uppercase tracking-wide text-neutral-600 dark:text-neutral-400"
18950
19080
  >
18951
19081
  Step {{ activeIndex() + 1 }} of {{ steps().length }}
18952
19082
  <span
@@ -18975,7 +19105,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18975
19105
  <li class="flex min-w-0 shrink-0 items-center gap-1">
18976
19106
  <button
18977
19107
  type="button"
18978
- class="inline-flex max-w-38 items-center gap-2 rounded-lg px-2 py-1.5 text-left transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink disabled:cursor-not-allowed"
19108
+ class="inline-flex max-w-38 items-center gap-2 rounded-lg px-2 py-1.5 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ink disabled:cursor-not-allowed"
18979
19109
  [class]="headerButtonClass(i)"
18980
19110
  [attr.aria-current]="i === activeIndex() ? 'step' : null"
18981
19111
  [disabled]="!canActivate(i)"
@@ -19014,7 +19144,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
19014
19144
  }
19015
19145
  </div>
19016
19146
  </div>
19017
- `, styles: [".africanies-step-rail{scrollbar-width:thin}.africanies-step-body{min-width:0;overflow-x:clip;padding-inline:2px;margin-inline:-2px}.africanies-step-pane{min-width:0;will-change:opacity,transform}\n"] }]
19147
+ `, styles: [".africanies-step-rail{scrollbar-width:thin}.africanies-step-body{min-width:0;overflow-x:clip;padding-inline:4px;margin-inline:-4px}.africanies-step-pane{min-width:0;will-change:opacity,transform}\n"] }]
19018
19148
  }], ctorParameters: () => [], propDecorators: { steps: [{ type: i0.Input, args: [{ isSignal: true, alias: "steps", required: true }] }], activeIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeIndex", required: true }] }], linear: [{ type: i0.Input, args: [{ isSignal: true, alias: "linear", required: false }] }], animateSteps: [{ type: i0.Input, args: [{ isSignal: true, alias: "animateSteps", required: false }] }], activeIndexChange: [{ type: i0.Output, args: ["activeIndexChange"] }], stepDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => StepDefDirective), { isSignal: true }] }], stepPane: [{ type: i0.ViewChild, args: ['stepPane', { isSignal: true }] }] } });
19019
19149
 
19020
19150
  /**