@africanies/angular-web-sdk 0.1.7 → 0.1.9

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 (157) hide show
  1. package/.angular/cache/22.0.9/playground/.tsbuildinfo +1 -1
  2. package/.nx/cache/{4082520855419352840 → 15302120112746851896}/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +30 -8
  3. package/.nx/cache/15302120112746851896/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -0
  4. package/.nx/cache/{4082520855419352840 → 15302120112746851896}/dist/libs/africanies-theme/tailwind-preset.cjs +89 -12
  5. package/.nx/cache/{4082520855419352840 → 15302120112746851896}/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +14 -4
  6. package/.nx/cache/17289624717336768573/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +4639 -0
  7. package/.nx/cache/17289624717336768573/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +1 -0
  8. package/.nx/cache/17289624717336768573/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +2748 -0
  9. package/.nx/cache/18393832731522001121/dist/libs/africanies-core/README.md +85 -0
  10. package/.nx/cache/{17491371155400585077 → 18393832731522001121}/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +139 -51
  11. package/.nx/cache/18393832731522001121/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +1 -0
  12. package/.nx/cache/18393832731522001121/dist/libs/africanies-core/package.json +31 -0
  13. package/.nx/cache/{17491371155400585077 → 18393832731522001121}/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +14 -11
  14. package/.nx/cache/{3314955264390318442 → 3953970051755345887}/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +775 -566
  15. package/.nx/cache/3953970051755345887/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -0
  16. package/.nx/cache/{17499961385238663835 → 3953970051755345887}/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +150 -119
  17. package/.nx/cache/{12276756491762927325 → 4225324930345532464}/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +34 -6
  18. package/.nx/cache/4225324930345532464/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -0
  19. package/.nx/cache/{12276756491762927325 → 4225324930345532464}/dist/libs/africanies-theme/tailwind-preset.cjs +104 -19
  20. package/.nx/cache/{12276756491762927325 → 4225324930345532464}/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +21 -2
  21. package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/README.md +69 -0
  22. package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/assets/icons.sprite.svg +1851 -0
  23. package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs +833 -0
  24. package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs.map +1 -0
  25. package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/package.json +30 -0
  26. package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/types/africanies-africanies-icons.d.ts +108 -0
  27. package/.nx/cache/877026836685850193/dist/libs/africanies-models/README.md +60 -0
  28. package/.nx/cache/877026836685850193/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs +613 -0
  29. package/.nx/cache/877026836685850193/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs.map +1 -0
  30. package/.nx/cache/877026836685850193/dist/libs/africanies-models/package.json +23 -0
  31. package/.nx/cache/877026836685850193/dist/libs/africanies-models/types/africanies-africanies-models.d.ts +1452 -0
  32. package/.nx/cache/{17499961385238663835 → 9406544675747598148}/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +765 -615
  33. package/.nx/cache/9406544675747598148/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -0
  34. package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +170 -130
  35. package/.nx/cache/run.json +36 -36
  36. package/.nx/cache/terminalOutputs/{3017748574492080228 → 12565224028063674529} +16 -7
  37. package/.nx/cache/terminalOutputs/{8648260256613557249 → 13478445034325959094} +175 -172
  38. package/.nx/cache/terminalOutputs/{7126777490381568487 → 15149466893522345489} +10 -5
  39. package/.nx/cache/terminalOutputs/{4082520855419352840 → 15302120112746851896} +1 -1
  40. package/.nx/cache/terminalOutputs/15835338512221847842 +185 -0
  41. package/.nx/cache/terminalOutputs/{17491371155400585077 → 17289624717336768573} +1 -1
  42. package/.nx/cache/terminalOutputs/17571479482482478257 +35 -0
  43. package/.nx/cache/terminalOutputs/{9408529484792607601 → 17960163310445607219} +1 -1
  44. package/.nx/cache/terminalOutputs/18393832731522001121 +21 -0
  45. package/.nx/cache/terminalOutputs/3088264223473143358 +8 -0
  46. package/.nx/cache/terminalOutputs/{3314955264390318442 → 3953970051755345887} +1 -1
  47. package/.nx/cache/terminalOutputs/{12276756491762927325 → 4225324930345532464} +1 -1
  48. package/.nx/cache/terminalOutputs/4363587767476846758 +176 -0
  49. package/.nx/cache/terminalOutputs/5151121439688073678 +21 -0
  50. package/.nx/cache/terminalOutputs/7462873189468732296 +17 -0
  51. package/.nx/cache/terminalOutputs/8032148447904822353 +184 -182
  52. package/.nx/cache/terminalOutputs/8193462679279018087 +39 -0
  53. package/.nx/cache/terminalOutputs/877026836685850193 +21 -0
  54. package/.nx/cache/terminalOutputs/9406544675747598148 +21 -0
  55. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db +0 -0
  56. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-shm +0 -0
  57. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-wal +0 -0
  58. package/.nx/workspace-data/d/daemon.log +6813 -0
  59. package/.nx/workspace-data/d/server-process.json +2 -2
  60. package/.nx/workspace-data/file-map.json +3490 -3432
  61. package/.nx/workspace-data/nx_files.nxt +0 -0
  62. package/.nx/workspace-data/project-graph.json +7 -2
  63. package/apps/playground/src/app/pages/tokens-page.ts +17 -13
  64. package/apps/playground/src/app/snippets/filters.snippets.ts +10 -0
  65. package/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +6 -1
  66. package/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +1 -1
  67. package/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +6 -1
  68. package/dist/libs/africanies-icons/assets/icons.sprite.svg +7 -0
  69. package/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs +1 -0
  70. package/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs.map +1 -1
  71. package/dist/libs/africanies-icons/types/africanies-africanies-icons.d.ts +2 -2
  72. package/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs +22 -8
  73. package/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs.map +1 -1
  74. package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +22 -10
  75. package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -1
  76. package/dist/libs/africanies-theme/tailwind-preset.cjs +84 -16
  77. package/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +4 -2
  78. package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +723 -618
  79. package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -1
  80. package/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +142 -114
  81. package/libs/africanies-core/src/index.ts +1 -1
  82. package/libs/africanies-core/src/lib/notification/notification-link.ts +6 -1
  83. package/libs/africanies-icons/src/assets/icons.sprite.svg +7 -0
  84. package/libs/africanies-icons/src/lib/icon-name.ts +1 -0
  85. package/libs/africanies-models/src/lib/filters/configs/seed-configs.ts +22 -8
  86. package/libs/africanies-theme/src/lib/mode-color.safelist.ts +12 -0
  87. package/libs/africanies-theme/src/lib/mode-color.service.spec.ts +55 -0
  88. package/libs/africanies-theme/src/lib/mode-color.service.ts +11 -11
  89. package/libs/africanies-theme/tailwind-preset.cjs +84 -16
  90. package/libs/africanies-ui/src/lib/action-menu/action-menu.component.ts +3 -3
  91. package/libs/africanies-ui/src/lib/alert/alert.component.ts +4 -4
  92. package/libs/africanies-ui/src/lib/avatar/avatar-menu.component.ts +2 -2
  93. package/libs/africanies-ui/src/lib/button/button.component.spec.ts +7 -7
  94. package/libs/africanies-ui/src/lib/button/button.component.ts +5 -5
  95. package/libs/africanies-ui/src/lib/chip/chip.component.ts +4 -4
  96. package/libs/africanies-ui/src/lib/content-stack/content-stack.component.ts +1 -1
  97. package/libs/africanies-ui/src/lib/feedback/error-indicator.component.ts +3 -3
  98. package/libs/africanies-ui/src/lib/feedback/error-state.component.ts +2 -2
  99. package/libs/africanies-ui/src/lib/filters/filter-drawer.panel.ts +44 -27
  100. package/libs/africanies-ui/src/lib/forms/checkbox/checkbox.component.ts +2 -1
  101. package/libs/africanies-ui/src/lib/forms/file-upload/file-preview.dialog.ts +1 -1
  102. package/libs/africanies-ui/src/lib/forms/file-upload/file-upload.component.ts +1 -1
  103. package/libs/africanies-ui/src/lib/forms/form-field.classes.ts +36 -6
  104. package/libs/africanies-ui/src/lib/forms/otp-input/otp-input.component.ts +2 -1
  105. package/libs/africanies-ui/src/lib/forms/radio/radio.component.ts +2 -1
  106. package/libs/africanies-ui/src/lib/forms/select/select.component.ts +1 -1
  107. package/libs/africanies-ui/src/lib/forms/toggle/toggle.component.ts +1 -1
  108. package/libs/africanies-ui/src/lib/image/image.component.ts +1 -1
  109. package/libs/africanies-ui/src/lib/info-popover/info-popover.component.ts +2 -2
  110. package/libs/africanies-ui/src/lib/layout/app-shell-content-header.component.ts +1 -1
  111. package/libs/africanies-ui/src/lib/layout/app-shell-header.component.ts +14 -14
  112. package/libs/africanies-ui/src/lib/layout/app-shell.component.html +2 -2
  113. package/libs/africanies-ui/src/lib/layout/app-shell.component.ts +3 -3
  114. package/libs/africanies-ui/src/lib/layout/header-greeting.util.ts +0 -2
  115. package/libs/africanies-ui/src/lib/layout/header-weather.spec.ts +11 -40
  116. package/libs/africanies-ui/src/lib/layout/header-weather.ts +22 -63
  117. package/libs/africanies-ui/src/lib/navigation/shipping-mode-switch/shipping-mode-switch.component.ts +6 -6
  118. package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.html +21 -13
  119. package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.ts +13 -7
  120. package/libs/africanies-ui/src/lib/notifications/notification-drawer.panel.ts +6 -6
  121. package/libs/africanies-ui/src/lib/overlay/confirm-dialog.component.ts +2 -2
  122. package/libs/africanies-ui/src/lib/overlay/drawer.service.ts +2 -2
  123. package/libs/africanies-ui/src/lib/overlay/modal.service.ts +3 -3
  124. package/libs/africanies-ui/src/lib/overlay/overlay-frame.component.ts +1 -1
  125. package/libs/africanies-ui/src/lib/pagination/pagination.component.ts +1 -1
  126. package/libs/africanies-ui/src/lib/stepper/stepper.component.ts +7 -7
  127. package/libs/africanies-ui/src/lib/table/table-column.ts +9 -0
  128. package/libs/africanies-ui/src/lib/table/table.component.spec.ts +99 -0
  129. package/libs/africanies-ui/src/lib/table/table.component.ts +72 -30
  130. package/libs/africanies-ui/src/lib/toast/toast-item.component.ts +6 -6
  131. package/package.json +1 -1
  132. package/svg/hand-coins.svg +9 -0
  133. package/.nx/cache/12276756491762927325/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +0 -1
  134. package/.nx/cache/17491371155400585077/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +0 -1
  135. package/.nx/cache/17499961385238663835/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +0 -1
  136. package/.nx/cache/3314955264390318442/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +0 -1
  137. package/.nx/cache/4082520855419352840/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +0 -1
  138. package/.nx/cache/terminalOutputs/17499961385238663835 +0 -21
  139. /package/.nx/cache/{12276756491762927325 → 15302120112746851896}/dist/libs/africanies-theme/README.md +0 -0
  140. /package/.nx/cache/{12276756491762927325 → 15302120112746851896}/dist/libs/africanies-theme/package.json +0 -0
  141. /package/.nx/cache/{17491371155400585077 → 17289624717336768573}/dist/libs/africanies-core/README.md +0 -0
  142. /package/.nx/cache/{17491371155400585077 → 17289624717336768573}/dist/libs/africanies-core/package.json +0 -0
  143. /package/.nx/cache/{17499961385238663835 → 3953970051755345887}/dist/libs/africanies-ui/README.md +0 -0
  144. /package/.nx/cache/{17499961385238663835 → 3953970051755345887}/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
  145. /package/.nx/cache/{17499961385238663835 → 3953970051755345887}/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
  146. /package/.nx/cache/{17499961385238663835 → 3953970051755345887}/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +0 -0
  147. /package/.nx/cache/{17499961385238663835 → 3953970051755345887}/dist/libs/africanies-ui/assets/carriers/dhl.svg +0 -0
  148. /package/.nx/cache/{17499961385238663835 → 3953970051755345887}/dist/libs/africanies-ui/package.json +0 -0
  149. /package/.nx/cache/{4082520855419352840 → 4225324930345532464}/dist/libs/africanies-theme/README.md +0 -0
  150. /package/.nx/cache/{4082520855419352840 → 4225324930345532464}/dist/libs/africanies-theme/package.json +0 -0
  151. /package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/README.md +0 -0
  152. /package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
  153. /package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
  154. /package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +0 -0
  155. /package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/assets/carriers/dhl.svg +0 -0
  156. /package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/package.json +0 -0
  157. /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,24 +7003,49 @@ 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
- />
7213
- }
7214
- @case ('select') {
7215
- <!-- Label already shown in the section header — avoid double label. -->
7216
- <africanies-select
7217
- label=""
7218
- [searchable]="true"
7219
- [placeholder]="field.placeholder ?? 'Select…'"
7220
- [options]="selectOptionsFor(field)"
7221
- [selected]="selectedForField(field)"
7222
- [error]="optionErrorFor(field.key)"
7223
- [loading]="optionLoadingFor(field.key)"
7224
- [retrying]="optionLoadingFor(field.key)"
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>
7037
+ }
7038
+ @case ('select') {
7039
+ <!-- Label already shown in the section header — avoid double label. -->
7040
+ <africanies-select
7041
+ label=""
7042
+ [searchable]="true"
7043
+ [placeholder]="field.placeholder ?? 'Select…'"
7044
+ [options]="selectOptionsFor(field)"
7045
+ [selected]="selectedForField(field)"
7046
+ [error]="optionErrorFor(field.key)"
7047
+ [loading]="optionLoadingFor(field.key)"
7048
+ [retrying]="optionLoadingFor(field.key)"
7225
7049
  [showRetry]="true"
7226
7050
  (retry)="retryFieldOptions(field.key)"
7227
7051
  (selectedChange)="onSelectChange(field.key, $event)"
@@ -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
  ],
@@ -7473,12 +7296,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
7473
7296
  </div>
7474
7297
  }
7475
7298
  @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
- />
7299
+ <div class="flex flex-wrap gap-2">
7300
+ @for (opt of booleanOptions(field); track opt.value) {
7301
+ <button
7302
+ type="button"
7303
+ class="cursor-pointer rounded-md border px-2.5 py-1.5 text-body-sm transition-colors"
7304
+ [style.color]="
7305
+ draft().values[field.key] === opt.value
7306
+ ? opt.color ?? null
7307
+ : null
7308
+ "
7309
+ [style.border-color]="
7310
+ draft().values[field.key] === opt.value
7311
+ ? opt.color ?? null
7312
+ : null
7313
+ "
7314
+ [style.background-color]="
7315
+ draft().values[field.key] === opt.value && opt.color
7316
+ ? opt.color + '1A'
7317
+ : null
7318
+ "
7319
+ [class]="
7320
+ draft().values[field.key] === opt.value
7321
+ ? 'font-medium'
7322
+ : 'border-neutral-300 text-neutral-600 hover:border-neutral-400 dark:border-white/15 dark:text-neutral-300'
7323
+ "
7324
+ (click)="setFieldValue(field.key, opt.value)"
7325
+ >
7326
+ {{ opt.label }}
7327
+ </button>
7328
+ }
7329
+ </div>
7482
7330
  }
7483
7331
  @case ('select') {
7484
7332
  <!-- Label already shown in the section header — avoid double label. -->
@@ -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
  }
@@ -11459,48 +11307,314 @@ class OtpInputComponent {
11459
11307
  el?.select();
11460
11308
  });
11461
11309
  }
11462
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: OtpInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11463
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: OtpInputComponent, isStandalone: true, selector: "africanies-otp-input", 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 }, length: { classPropertyName: "length", publicName: "length", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", 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 }, showResend: { classPropertyName: "showResend", publicName: "showResend", isSignal: true, isRequired: false, transformFunction: null }, resendPrompt: { classPropertyName: "resendPrompt", publicName: "resendPrompt", isSignal: true, isRequired: false, transformFunction: null }, resendLabel: { classPropertyName: "resendLabel", publicName: "resendLabel", isSignal: true, isRequired: false, transformFunction: null }, resendCooldown: { classPropertyName: "resendCooldown", publicName: "resendCooldown", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", completed: "completed", resend: "resend" }, host: { classAttribute: "block" }, providers: [
11310
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: OtpInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11311
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: OtpInputComponent, isStandalone: true, selector: "africanies-otp-input", 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 }, length: { classPropertyName: "length", publicName: "length", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", 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 }, showResend: { classPropertyName: "showResend", publicName: "showResend", isSignal: true, isRequired: false, transformFunction: null }, resendPrompt: { classPropertyName: "resendPrompt", publicName: "resendPrompt", isSignal: true, isRequired: false, transformFunction: null }, resendLabel: { classPropertyName: "resendLabel", publicName: "resendLabel", isSignal: true, isRequired: false, transformFunction: null }, resendCooldown: { classPropertyName: "resendCooldown", publicName: "resendCooldown", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", completed: "completed", resend: "resend" }, host: { classAttribute: "block" }, providers: [
11312
+ {
11313
+ provide: NG_VALUE_ACCESSOR,
11314
+ useExisting: forwardRef(() => OtpInputComponent),
11315
+ multi: true,
11316
+ },
11317
+ ], viewQueries: [{ propertyName: "cellInputs", predicate: ["cellInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
11318
+ <div
11319
+ role="group"
11320
+ [attr.aria-labelledby]="label() ? labelId : null"
11321
+ [attr.aria-describedby]="describedBy()"
11322
+ [attr.aria-disabled]="disabled() ? true : null"
11323
+ >
11324
+ @if (label()) {
11325
+ <p [id]="labelId" [class]="labelClass">{{ label() }}</p>
11326
+ }
11327
+ <div
11328
+ class="flex flex-wrap gap-2"
11329
+ [class.opacity-50]="disabled()"
11330
+ [class.cursor-not-allowed]="disabled()"
11331
+ >
11332
+ @for (digit of cells(); track $index; let i = $index) {
11333
+ <input
11334
+ #cellInput
11335
+ [type]="cellType()"
11336
+ inputmode="numeric"
11337
+ pattern="[0-9]*"
11338
+ maxlength="1"
11339
+ [class]="cellClass()"
11340
+ [id]="i === 0 ? controlId : null"
11341
+ [value]="digit"
11342
+ [disabled]="disabled()"
11343
+ [attr.autocomplete]="i === 0 ? 'one-time-code' : 'off'"
11344
+ [attr.aria-label]="'Digit ' + (i + 1) + ' of ' + length()"
11345
+ [attr.aria-invalid]="error() ? true : null"
11346
+ (input)="onCellInput(i, $event)"
11347
+ (keydown)="onCellKeydown(i, $event)"
11348
+ (paste)="onPaste(i, $event)"
11349
+ (focus)="onCellFocus(i)"
11350
+ (blur)="onBlur()"
11351
+ />
11352
+ }
11353
+ </div>
11354
+ @if (error(); as err) {
11355
+ <p [id]="errorId" [class]="errorClass" role="alert">{{ err }}</p>
11356
+ } @else if (hint(); as h) {
11357
+ <p [id]="hintId" [class]="hintClass">{{ h }}</p>
11358
+ }
11359
+
11360
+ @if (showResend()) {
11361
+ <div
11362
+ class="mt-3 flex flex-wrap items-center gap-x-1.5 gap-y-1 text-body-sm text-neutral-600 dark:text-neutral-400"
11363
+ >
11364
+ <span>{{ resendPrompt() }}</span>
11365
+ @if (cooldownRemaining() > 0) {
11366
+ <span class="tabular-nums" aria-live="polite">
11367
+ Resend in {{ cooldownRemaining() }}s
11368
+ </span>
11369
+ } @else {
11370
+ <button
11371
+ africanies-button
11372
+ type="button"
11373
+ variant="ghost"
11374
+ size="sm"
11375
+ class="!min-h-0 !px-1.5 !py-0.5 !text-body-sm"
11376
+ [disabled]="disabled()"
11377
+ (click)="onResendClick()"
11378
+ >
11379
+ {{ resendLabel() }}
11380
+ </button>
11381
+ }
11382
+ </div>
11383
+ }
11384
+ </div>
11385
+ `, isInline: true, styles: [".africanies-otp-cell-masked{-webkit-text-security:disc;text-security:disc}\n"], dependencies: [{ kind: "component", type: ButtonComponent, selector: "button[africanies-button], a[africanies-button]", inputs: ["variant", "size", "disabled", "loading"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11386
+ }
11387
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: OtpInputComponent, decorators: [{
11388
+ type: Component,
11389
+ args: [{ selector: 'africanies-otp-input', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ButtonComponent], providers: [
11390
+ {
11391
+ provide: NG_VALUE_ACCESSOR,
11392
+ useExisting: forwardRef(() => OtpInputComponent),
11393
+ multi: true,
11394
+ },
11395
+ ], host: {
11396
+ class: 'block',
11397
+ }, template: `
11398
+ <div
11399
+ role="group"
11400
+ [attr.aria-labelledby]="label() ? labelId : null"
11401
+ [attr.aria-describedby]="describedBy()"
11402
+ [attr.aria-disabled]="disabled() ? true : null"
11403
+ >
11404
+ @if (label()) {
11405
+ <p [id]="labelId" [class]="labelClass">{{ label() }}</p>
11406
+ }
11407
+ <div
11408
+ class="flex flex-wrap gap-2"
11409
+ [class.opacity-50]="disabled()"
11410
+ [class.cursor-not-allowed]="disabled()"
11411
+ >
11412
+ @for (digit of cells(); track $index; let i = $index) {
11413
+ <input
11414
+ #cellInput
11415
+ [type]="cellType()"
11416
+ inputmode="numeric"
11417
+ pattern="[0-9]*"
11418
+ maxlength="1"
11419
+ [class]="cellClass()"
11420
+ [id]="i === 0 ? controlId : null"
11421
+ [value]="digit"
11422
+ [disabled]="disabled()"
11423
+ [attr.autocomplete]="i === 0 ? 'one-time-code' : 'off'"
11424
+ [attr.aria-label]="'Digit ' + (i + 1) + ' of ' + length()"
11425
+ [attr.aria-invalid]="error() ? true : null"
11426
+ (input)="onCellInput(i, $event)"
11427
+ (keydown)="onCellKeydown(i, $event)"
11428
+ (paste)="onPaste(i, $event)"
11429
+ (focus)="onCellFocus(i)"
11430
+ (blur)="onBlur()"
11431
+ />
11432
+ }
11433
+ </div>
11434
+ @if (error(); as err) {
11435
+ <p [id]="errorId" [class]="errorClass" role="alert">{{ err }}</p>
11436
+ } @else if (hint(); as h) {
11437
+ <p [id]="hintId" [class]="hintClass">{{ h }}</p>
11438
+ }
11439
+
11440
+ @if (showResend()) {
11441
+ <div
11442
+ class="mt-3 flex flex-wrap items-center gap-x-1.5 gap-y-1 text-body-sm text-neutral-600 dark:text-neutral-400"
11443
+ >
11444
+ <span>{{ resendPrompt() }}</span>
11445
+ @if (cooldownRemaining() > 0) {
11446
+ <span class="tabular-nums" aria-live="polite">
11447
+ Resend in {{ cooldownRemaining() }}s
11448
+ </span>
11449
+ } @else {
11450
+ <button
11451
+ africanies-button
11452
+ type="button"
11453
+ variant="ghost"
11454
+ size="sm"
11455
+ class="!min-h-0 !px-1.5 !py-0.5 !text-body-sm"
11456
+ [disabled]="disabled()"
11457
+ (click)="onResendClick()"
11458
+ >
11459
+ {{ resendLabel() }}
11460
+ </button>
11461
+ }
11462
+ </div>
11463
+ }
11464
+ </div>
11465
+ `, styles: [".africanies-otp-cell-masked{-webkit-text-security:disc;text-security:disc}\n"] }]
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"] }] } });
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: [
11464
11578
  {
11465
11579
  provide: NG_VALUE_ACCESSOR,
11466
- useExisting: forwardRef(() => OtpInputComponent),
11580
+ useExisting: forwardRef(() => RadioComponent),
11467
11581
  multi: true,
11468
11582
  },
11469
- ], viewQueries: [{ propertyName: "cellInputs", predicate: ["cellInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
11470
- <div
11471
- role="group"
11472
- [attr.aria-labelledby]="label() ? labelId : null"
11583
+ ], ngImport: i0, template: `
11584
+ <fieldset
11585
+ class="m-0 p-0 border-0 min-w-0"
11586
+ [attr.aria-invalid]="error() ? true : null"
11473
11587
  [attr.aria-describedby]="describedBy()"
11474
- [attr.aria-disabled]="disabled() ? true : null"
11475
11588
  >
11476
- @if (label()) {
11477
- <p [id]="labelId" [class]="labelClass">{{ label() }}</p>
11478
- }
11479
- <div
11480
- class="flex flex-wrap gap-2"
11481
- [class.opacity-50]="disabled()"
11482
- [class.cursor-not-allowed]="disabled()"
11483
- >
11484
- @for (digit of cells(); track $index; let i = $index) {
11485
- <input
11486
- #cellInput
11487
- [type]="cellType()"
11488
- inputmode="numeric"
11489
- pattern="[0-9]*"
11490
- maxlength="1"
11491
- [class]="cellClass()"
11492
- [id]="i === 0 ? controlId : null"
11493
- [value]="digit"
11494
- [disabled]="disabled()"
11495
- [attr.autocomplete]="i === 0 ? 'one-time-code' : 'off'"
11496
- [attr.aria-label]="'Digit ' + (i + 1) + ' of ' + length()"
11497
- [attr.aria-invalid]="error() ? true : null"
11498
- (input)="onCellInput(i, $event)"
11499
- (keydown)="onCellKeydown(i, $event)"
11500
- (paste)="onPaste(i, $event)"
11501
- (focus)="onCellFocus(i)"
11502
- (blur)="onBlur()"
11503
- />
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>
11504
11618
  }
11505
11619
  </div>
11506
11620
  @if (error(); as err) {
@@ -11508,79 +11622,60 @@ class OtpInputComponent {
11508
11622
  } @else if (hint(); as h) {
11509
11623
  <p [id]="hintId" [class]="hintClass">{{ h }}</p>
11510
11624
  }
11511
-
11512
- @if (showResend()) {
11513
- <div
11514
- class="mt-3 flex flex-wrap items-center gap-x-1.5 gap-y-1 text-body-sm text-neutral-600 dark:text-neutral-400"
11515
- >
11516
- <span>{{ resendPrompt() }}</span>
11517
- @if (cooldownRemaining() > 0) {
11518
- <span class="tabular-nums" aria-live="polite">
11519
- Resend in {{ cooldownRemaining() }}s
11520
- </span>
11521
- } @else {
11522
- <button
11523
- africanies-button
11524
- type="button"
11525
- variant="ghost"
11526
- size="sm"
11527
- class="!min-h-0 !px-1.5 !py-0.5 !text-body-sm"
11528
- [disabled]="disabled()"
11529
- (click)="onResendClick()"
11530
- >
11531
- {{ resendLabel() }}
11532
- </button>
11533
- }
11534
- </div>
11535
- }
11536
- </div>
11537
- `, isInline: true, styles: [".africanies-otp-cell-masked{-webkit-text-security:disc;text-security:disc}\n"], dependencies: [{ kind: "component", type: ButtonComponent, selector: "button[africanies-button], a[africanies-button]", inputs: ["variant", "size", "disabled", "loading"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11625
+ </fieldset>
11626
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
11538
11627
  }
11539
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: OtpInputComponent, decorators: [{
11628
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: RadioComponent, decorators: [{
11540
11629
  type: Component,
11541
- args: [{ selector: 'africanies-otp-input', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ButtonComponent], providers: [
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: [
11542
11638
  {
11543
11639
  provide: NG_VALUE_ACCESSOR,
11544
- useExisting: forwardRef(() => OtpInputComponent),
11640
+ useExisting: forwardRef(() => RadioComponent),
11545
11641
  multi: true,
11546
11642
  },
11547
- ], host: {
11548
- class: 'block',
11549
- }, template: `
11550
- <div
11551
- role="group"
11552
- [attr.aria-labelledby]="label() ? labelId : null"
11643
+ ],
11644
+ template: `
11645
+ <fieldset
11646
+ class="m-0 p-0 border-0 min-w-0"
11647
+ [attr.aria-invalid]="error() ? true : null"
11553
11648
  [attr.aria-describedby]="describedBy()"
11554
- [attr.aria-disabled]="disabled() ? true : null"
11555
11649
  >
11556
- @if (label()) {
11557
- <p [id]="labelId" [class]="labelClass">{{ label() }}</p>
11558
- }
11559
- <div
11560
- class="flex flex-wrap gap-2"
11561
- [class.opacity-50]="disabled()"
11562
- [class.cursor-not-allowed]="disabled()"
11563
- >
11564
- @for (digit of cells(); track $index; let i = $index) {
11565
- <input
11566
- #cellInput
11567
- [type]="cellType()"
11568
- inputmode="numeric"
11569
- pattern="[0-9]*"
11570
- maxlength="1"
11571
- [class]="cellClass()"
11572
- [id]="i === 0 ? controlId : null"
11573
- [value]="digit"
11574
- [disabled]="disabled()"
11575
- [attr.autocomplete]="i === 0 ? 'one-time-code' : 'off'"
11576
- [attr.aria-label]="'Digit ' + (i + 1) + ' of ' + length()"
11577
- [attr.aria-invalid]="error() ? true : null"
11578
- (input)="onCellInput(i, $event)"
11579
- (keydown)="onCellKeydown(i, $event)"
11580
- (paste)="onPaste(i, $event)"
11581
- (focus)="onCellFocus(i)"
11582
- (blur)="onBlur()"
11583
- />
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>
11584
11679
  }
11585
11680
  </div>
11586
11681
  @if (error(); as err) {
@@ -11588,34 +11683,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
11588
11683
  } @else if (hint(); as h) {
11589
11684
  <p [id]="hintId" [class]="hintClass">{{ h }}</p>
11590
11685
  }
11591
-
11592
- @if (showResend()) {
11593
- <div
11594
- class="mt-3 flex flex-wrap items-center gap-x-1.5 gap-y-1 text-body-sm text-neutral-600 dark:text-neutral-400"
11595
- >
11596
- <span>{{ resendPrompt() }}</span>
11597
- @if (cooldownRemaining() > 0) {
11598
- <span class="tabular-nums" aria-live="polite">
11599
- Resend in {{ cooldownRemaining() }}s
11600
- </span>
11601
- } @else {
11602
- <button
11603
- africanies-button
11604
- type="button"
11605
- variant="ghost"
11606
- size="sm"
11607
- class="!min-h-0 !px-1.5 !py-0.5 !text-body-sm"
11608
- [disabled]="disabled()"
11609
- (click)="onResendClick()"
11610
- >
11611
- {{ resendLabel() }}
11612
- </button>
11613
- }
11614
- </div>
11615
- }
11616
- </div>
11617
- `, styles: [".africanies-otp-cell-masked{-webkit-text-security:disc;text-security:disc}\n"] }]
11618
- }], 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"] }] } });
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 }] }] } });
11619
11690
 
11620
11691
  /**
11621
11692
  * Radio form control exports.
@@ -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" />
@@ -16015,14 +16091,12 @@ function hashString(value) {
16015
16091
  return hash;
16016
16092
  }
16017
16093
 
16018
- /** Bump when cache shape or city resolution strategy changes. */
16019
- const CACHE_KEY = 'africanies-header-weather-v4';
16094
+ /** Bump when cache shape or lookup strategy changes. */
16095
+ const CACHE_KEY = 'africanies-header-weather-v5';
16020
16096
  const FETCH_MS = 4_000;
16021
16097
  const BROWSER_GEO_MS = 3_500;
16022
- /** IP fallback — city string is often wrong (e.g. NG IPs labelled Lagos). */
16098
+ /** IP fallback for coordinates only — city strings are often wrong. */
16023
16099
  const GEO_URL = 'https://get.geojs.io/v1/ip/geo.json';
16024
- /** Client reverse-geocode so the label matches the forecast coordinates. */
16025
- const REVERSE_GEO_URL = 'https://api.bigdatacloud.net/data/reverse-geocode-client';
16026
16100
  const WEATHER_LABELS = {
16027
16101
  clear: 'Clear',
16028
16102
  cloudy: 'Cloudy',
@@ -16066,9 +16140,13 @@ function mapWmoWeatherCode(code) {
16066
16140
  * Short condition label for the header weather chip.
16067
16141
  *
16068
16142
  * @param kind - Coarse forecast kind.
16069
- * @returns Display label such as `Rain` or `Clear`.
16143
+ * @param hour - Local hour; clear days show as Sunny, nights as Clear.
16144
+ * @returns Display label such as `Rain` or `Sunny`.
16070
16145
  */
16071
- function headerWeatherLabel(kind) {
16146
+ function headerWeatherLabel(kind, hour = new Date().getHours()) {
16147
+ if (kind === 'clear') {
16148
+ return hour >= 19 || hour < 6 ? 'Clear' : 'Sunny';
16149
+ }
16072
16150
  return WEATHER_LABELS[kind];
16073
16151
  }
16074
16152
  /**
@@ -16088,12 +16166,12 @@ function headerWeatherIcon(kind, hour) {
16088
16166
  return 'cloud-o';
16089
16167
  }
16090
16168
  /**
16091
- * City-level forecast via browser / IP geolocation + Open-Meteo (no API key).
16169
+ * Local forecast via browser / IP coordinates + Open-Meteo (no API key).
16092
16170
  *
16093
- * Coordinates prefer the browser Geolocation API (accurate for travellers;
16094
- * may prompt once). Otherwise IP geo is used. The city label is reverse-
16095
- * geocoded from those coordinates IP `city` strings are often wrong for
16096
- * Nigeria (many networks resolve as Lagos).
16171
+ * Coordinates prefer the browser Geolocation API (may prompt once).
16172
+ * Otherwise IP geo is used for lat/lon only — city labels are not shown
16173
+ * because IP and reverse-geocode cities are often wrong (e.g. NG IPs
16174
+ * labelled Lagos).
16097
16175
  *
16098
16176
  * Fails closed: missing browser APIs, timeouts, and HTTP errors all return
16099
16177
  * `null` so the greeting can stay time-of-day only. Cached per local hour
@@ -16130,10 +16208,8 @@ async function loadHeaderWeather(fetchFn) {
16130
16208
  return null;
16131
16209
  }
16132
16210
  const temperatureC = asNumber(forecast?.current?.temperature_2m) ?? undefined;
16133
- const city = (await reverseGeocodeCity(run, coords.latitude, coords.longitude)) ??
16134
- coords.fallbackCity;
16135
- const weather = { kind, temperatureC, city };
16136
- writeCache({ hour, kind, temperatureC, city });
16211
+ const weather = { kind, temperatureC };
16212
+ writeCache({ hour, kind, temperatureC });
16137
16213
  return weather;
16138
16214
  }
16139
16215
  catch {
@@ -16159,7 +16235,6 @@ function readCache(hour) {
16159
16235
  return {
16160
16236
  kind: parsed.kind,
16161
16237
  temperatureC: parsed.temperatureC,
16162
- city: parsed.city,
16163
16238
  };
16164
16239
  }
16165
16240
  catch {
@@ -16176,8 +16251,7 @@ function writeCache(value) {
16176
16251
  }
16177
16252
  /**
16178
16253
  * Prefer device coordinates when available (may prompt once); otherwise fall
16179
- * back to IP geo. IP city labels for Nigeria are often wrong — reverse
16180
- * geocoding from these coordinates still depends on accurate lat/lon.
16254
+ * back to IP lat/lon for the forecast only.
16181
16255
  */
16182
16256
  async function resolveCoordinates(fetchFn) {
16183
16257
  const browser = await tryBrowserGeolocation();
@@ -16190,11 +16264,7 @@ async function resolveCoordinates(fetchFn) {
16190
16264
  if (latitude === null || longitude === null) {
16191
16265
  return null;
16192
16266
  }
16193
- return {
16194
- latitude,
16195
- longitude,
16196
- fallbackCity: asCity(geo?.city),
16197
- };
16267
+ return { latitude, longitude };
16198
16268
  }
16199
16269
  async function tryBrowserGeolocation() {
16200
16270
  if (typeof navigator === 'undefined' ||
@@ -16219,15 +16289,6 @@ async function tryBrowserGeolocation() {
16219
16289
  });
16220
16290
  });
16221
16291
  }
16222
- async function reverseGeocodeCity(fetchFn, latitude, longitude) {
16223
- const url = `${REVERSE_GEO_URL}?latitude=${encodeURIComponent(String(latitude))}` +
16224
- `&longitude=${encodeURIComponent(String(longitude))}` +
16225
- `&localityLanguage=en`;
16226
- const payload = (await fetchJson(fetchFn, url));
16227
- return (asCity(payload?.city) ??
16228
- asCity(payload?.locality) ??
16229
- asCity(payload?.principalSubdivision));
16230
- }
16231
16292
  function asNumber(value) {
16232
16293
  if (typeof value === 'number' && Number.isFinite(value)) {
16233
16294
  return value;
@@ -16238,16 +16299,6 @@ function asNumber(value) {
16238
16299
  }
16239
16300
  return null;
16240
16301
  }
16241
- function asCity(value) {
16242
- if (typeof value !== 'string') {
16243
- return undefined;
16244
- }
16245
- const trimmed = value.trim();
16246
- if (!trimmed || trimmed.toLowerCase() === 'n/a') {
16247
- return undefined;
16248
- }
16249
- return trimmed;
16250
- }
16251
16302
  async function fetchJson(fetchFn, url) {
16252
16303
  const controller = new AbortController();
16253
16304
  const timer = setTimeout(() => controller.abort(), FETCH_MS);
@@ -16380,27 +16431,27 @@ class AppShellHeaderComponent {
16380
16431
  if (temp === undefined || !Number.isFinite(temp)) {
16381
16432
  return null;
16382
16433
  }
16383
- return temp.toFixed(1);
16434
+ return String(Math.round(temp));
16384
16435
  }, /* @ts-ignore */
16385
16436
  ...(ngDevMode ? [{ debugName: "weatherTemp" }] : /* istanbul ignore next */ []));
16386
- weatherPlace = computed(() => {
16437
+ weatherCondition = computed(() => {
16387
16438
  const forecast = this.weather();
16388
16439
  if (!forecast) {
16389
16440
  return '';
16390
16441
  }
16391
- return forecast.city?.trim() || headerWeatherLabel(forecast.kind);
16442
+ return headerWeatherLabel(forecast.kind, this.now().getHours());
16392
16443
  }, /* @ts-ignore */
16393
- ...(ngDevMode ? [{ debugName: "weatherPlace" }] : /* istanbul ignore next */ []));
16444
+ ...(ngDevMode ? [{ debugName: "weatherCondition" }] : /* istanbul ignore next */ []));
16394
16445
  weatherAriaLabel = computed(() => {
16395
16446
  const forecast = this.weather();
16396
16447
  if (!forecast) {
16397
16448
  return 'Weather';
16398
16449
  }
16399
- const condition = headerWeatherLabel(forecast.kind).toLowerCase();
16450
+ const condition = this.weatherCondition().toLowerCase();
16400
16451
  const temp = this.weatherTemp();
16401
- const city = forecast.city?.trim();
16402
- const degrees = temp === null ? condition : `${temp} degrees, ${condition}`;
16403
- return city ? `Weather in ${city}: ${degrees}` : `Weather: ${degrees}`;
16452
+ return temp === null
16453
+ ? `Weather: ${condition}`
16454
+ : `Weather: ${temp} degrees Celsius, ${condition}`;
16404
16455
  }, /* @ts-ignore */
16405
16456
  ...(ngDevMode ? [{ debugName: "weatherAriaLabel" }] : /* istanbul ignore next */ []));
16406
16457
  unreadCount = computed(() => (this.notifications() ?? []).filter((n) => !n.read).length, /* @ts-ignore */
@@ -16469,7 +16520,7 @@ class AppShellHeaderComponent {
16469
16520
  @if (greeting(); as hello) {
16470
16521
  <div class="min-w-0 overflow-hidden">
16471
16522
  <p
16472
- class="m-0 truncate text-caption leading-none text-neutral-500 dark:text-neutral-400"
16523
+ class="m-0 truncate text-caption leading-none text-neutral-600 dark:text-neutral-400"
16473
16524
  >
16474
16525
  {{ hello.kicker }}
16475
16526
  </p>
@@ -16495,19 +16546,19 @@ class AppShellHeaderComponent {
16495
16546
  <africanies-icon
16496
16547
  [name]="weatherIcon()"
16497
16548
  [size]="18"
16498
- class="text-neutral-500 dark:text-neutral-400"
16549
+ class="text-neutral-600 dark:text-neutral-400"
16499
16550
  />
16500
16551
  @if (weatherTemp(); as temp) {
16501
16552
  <span
16502
16553
  class="text-body-sm font-medium tabular-nums tracking-tight text-ink dark:text-white"
16503
16554
  >
16504
- {{ temp }}°
16555
+ {{ temp }}°C
16505
16556
  </span>
16506
16557
  }
16507
16558
  <span
16508
- class="hidden text-caption text-neutral-500 md:inline dark:text-neutral-400"
16559
+ class="hidden text-caption text-neutral-600 md:inline dark:text-neutral-400"
16509
16560
  >
16510
- {{ weatherPlace() }}
16561
+ {{ weatherCondition() }}
16511
16562
  </span>
16512
16563
  </div>
16513
16564
  }
@@ -16515,7 +16566,7 @@ class AppShellHeaderComponent {
16515
16566
  @if (showClock()) {
16516
16567
  <div class="flex items-baseline gap-2" aria-live="polite">
16517
16568
  <time
16518
- class="hidden text-caption text-neutral-500 md:inline dark:text-neutral-400"
16569
+ class="hidden text-caption text-neutral-600 md:inline dark:text-neutral-400"
16519
16570
  [dateTime]="nowIso()"
16520
16571
  >
16521
16572
  {{ now() | date: clockDateFormat() }}
@@ -16545,7 +16596,7 @@ class AppShellHeaderComponent {
16545
16596
  @if (showNotifications()) {
16546
16597
  <button
16547
16598
  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"
16599
+ 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
16600
  [attr.aria-label]="notificationAriaLabel()"
16550
16601
  (click)="openNotifications()"
16551
16602
  >
@@ -16596,7 +16647,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16596
16647
  @if (greeting(); as hello) {
16597
16648
  <div class="min-w-0 overflow-hidden">
16598
16649
  <p
16599
- class="m-0 truncate text-caption leading-none text-neutral-500 dark:text-neutral-400"
16650
+ class="m-0 truncate text-caption leading-none text-neutral-600 dark:text-neutral-400"
16600
16651
  >
16601
16652
  {{ hello.kicker }}
16602
16653
  </p>
@@ -16622,19 +16673,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16622
16673
  <africanies-icon
16623
16674
  [name]="weatherIcon()"
16624
16675
  [size]="18"
16625
- class="text-neutral-500 dark:text-neutral-400"
16676
+ class="text-neutral-600 dark:text-neutral-400"
16626
16677
  />
16627
16678
  @if (weatherTemp(); as temp) {
16628
16679
  <span
16629
16680
  class="text-body-sm font-medium tabular-nums tracking-tight text-ink dark:text-white"
16630
16681
  >
16631
- {{ temp }}°
16682
+ {{ temp }}°C
16632
16683
  </span>
16633
16684
  }
16634
16685
  <span
16635
- class="hidden text-caption text-neutral-500 md:inline dark:text-neutral-400"
16686
+ class="hidden text-caption text-neutral-600 md:inline dark:text-neutral-400"
16636
16687
  >
16637
- {{ weatherPlace() }}
16688
+ {{ weatherCondition() }}
16638
16689
  </span>
16639
16690
  </div>
16640
16691
  }
@@ -16642,7 +16693,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16642
16693
  @if (showClock()) {
16643
16694
  <div class="flex items-baseline gap-2" aria-live="polite">
16644
16695
  <time
16645
- class="hidden text-caption text-neutral-500 md:inline dark:text-neutral-400"
16696
+ class="hidden text-caption text-neutral-600 md:inline dark:text-neutral-400"
16646
16697
  [dateTime]="nowIso()"
16647
16698
  >
16648
16699
  {{ now() | date: clockDateFormat() }}
@@ -16672,7 +16723,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16672
16723
  @if (showNotifications()) {
16673
16724
  <button
16674
16725
  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"
16726
+ 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
16727
  [attr.aria-label]="notificationAriaLabel()"
16677
16728
  (click)="openNotifications()"
16678
16729
  >
@@ -16874,14 +16925,14 @@ class AppShellComponent {
16874
16925
  // Preview embeds: absolute/h-full so the drawer stays inside the frame.
16875
16926
  // Production: h-dvh so the rail fills the viewport (h-full only matches parent).
16876
16927
  if (preview === 'desktop') {
16877
- return 'z-50 shrink-0 self-start sticky top-0 block h-full';
16928
+ return 'z-50 shrink-0 self-start sticky top-0 block h-full min-h-0';
16878
16929
  }
16879
16930
  if (preview === 'mobile' || preview === 'tablet') {
16880
16931
  return open
16881
- ? 'z-50 shrink-0 self-start absolute inset-y-0 left-0 h-full shadow-xl'
16932
+ ? 'z-50 shrink-0 self-start absolute inset-y-0 left-0 h-full min-h-0 shadow-xl'
16882
16933
  : 'hidden';
16883
16934
  }
16884
- const base = 'z-50 shrink-0 self-start h-dvh';
16935
+ const base = 'z-50 shrink-0 self-start h-dvh min-h-0';
16885
16936
  if (open) {
16886
16937
  return `${base} fixed inset-y-0 left-0 shadow-xl lg:sticky lg:top-0 lg:block`;
16887
16938
  }
@@ -16917,13 +16968,13 @@ class AppShellComponent {
16917
16968
  this.mobileNavOpen.set(false);
16918
16969
  }
16919
16970
  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 });
16971
+ 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
16972
  }
16922
16973
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AppShellComponent, decorators: [{
16923
16974
  type: Component,
16924
16975
  args: [{ selector: 'africanies-app-shell', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AfricaniesIconComponent, AppShellContentHeaderComponent, AppShellHeaderComponent], host: {
16925
16976
  '[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>" }]
16977
+ }, 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
16978
  }], 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
16979
 
16929
16980
  /**
@@ -17051,7 +17102,7 @@ class ImageComponent {
17051
17102
 
17052
17103
  @if (showPlaceholder()) {
17053
17104
  <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"
17105
+ class="absolute inset-0 flex items-center justify-center bg-neutral-200 text-neutral-600 dark:bg-white/10 dark:text-neutral-400"
17055
17106
  [class.animate-pulse]="pending()"
17056
17107
  aria-hidden="true"
17057
17108
  >
@@ -17107,7 +17158,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17107
17158
 
17108
17159
  @if (showPlaceholder()) {
17109
17160
  <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"
17161
+ class="absolute inset-0 flex items-center justify-center bg-neutral-200 text-neutral-600 dark:bg-white/10 dark:text-neutral-400"
17111
17162
  [class.animate-pulse]="pending()"
17112
17163
  aria-hidden="true"
17113
17164
  >
@@ -17461,7 +17512,7 @@ class PaginationComponent {
17461
17512
  @for (item of pageItems(); track trackPageItem(item, $index)) {
17462
17513
  @if (item === 'ellipsis') {
17463
17514
  <span
17464
- class="inline-flex min-w-8 items-center justify-center text-body-sm text-neutral-500 dark:text-neutral-400"
17515
+ class="inline-flex min-w-8 items-center justify-center text-body-sm text-neutral-600 dark:text-neutral-400"
17465
17516
  aria-hidden="true"
17466
17517
  >
17467
17518
 
@@ -17562,7 +17613,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17562
17613
  @for (item of pageItems(); track trackPageItem(item, $index)) {
17563
17614
  @if (item === 'ellipsis') {
17564
17615
  <span
17565
- class="inline-flex min-w-8 items-center justify-center text-body-sm text-neutral-500 dark:text-neutral-400"
17616
+ class="inline-flex min-w-8 items-center justify-center text-body-sm text-neutral-600 dark:text-neutral-400"
17566
17617
  aria-hidden="true"
17567
17618
  >
17568
17619
 
@@ -17612,11 +17663,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17612
17663
  * Hidden while the body shows empty or error — those states expose Retry instead.
17613
17664
  * While {@link refreshing} is true the rows stay on screen — the refresh icon
17614
17665
  * 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.
17666
+ * of data is changing (pagination / size): rows stay on screen, a keep-rows
17667
+ * overlay covers the grid, and the built-in pager shows a circular spinner
17668
+ * beside the page controls; the body loader appears only when there are no
17669
+ * rows yet (first load) or the host cleared rows for a shipping-mode switch
17670
+ * (STN ↔ SFN). Toolbar and column headers stay mounted.
17620
17671
  * top-right **Filters** then **Export** (`showFilter` / `showExport`). The table
17621
17672
  * does not own fetch, filter, or export logic — the host handles those events.
17622
17673
  *
@@ -17675,6 +17726,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17675
17726
  * to the left when rows are expandable. Override with
17676
17727
  * {@link TableColumn.sticky}. Sticky columns use a slightly thicker bottom
17677
17728
  * border (and opaque fill) so dividers stay visible while scrolling.
17729
+ *
17730
+ * **Sticky header (desktop):** when {@link stickyHeader} is true (default),
17731
+ * large screens scroll the grid inside a capped viewport and keep `th` pinned
17732
+ * to the top of that scrollport. Mobile keeps horizontal-only overflow so
17733
+ * page scroll is unchanged.
17678
17734
  */
17679
17735
  class TableComponent {
17680
17736
  modeColor = inject(ModeColorService);
@@ -17693,6 +17749,12 @@ class TableComponent {
17693
17749
  /** Column definitions (header chrome + keys). */
17694
17750
  columns = input.required(/* @ts-ignore */
17695
17751
  ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
17752
+ /**
17753
+ * When true (default), pin column headers while scrolling the grid on
17754
+ * large screens (`lg+`). Uses a capped scrollport so sticky works with
17755
+ * horizontal overflow. Set false to keep the previous unconstrained layout.
17756
+ */
17757
+ stickyHeader = input(true, { ...(ngDevMode ? { debugName: "stickyHeader" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
17696
17758
  /**
17697
17759
  * Row records to render. Empty arrays show {@link EmptyStateComponent} in
17698
17760
  * the body unless {@link loading} or {@link error} is set. Missing `data`
@@ -17723,9 +17785,9 @@ class TableComponent {
17723
17785
  refreshing = input(false, { ...(ngDevMode ? { debugName: "refreshing" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
17724
17786
  /**
17725
17787
  * 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
17788
+ * page size). When rows are already on screen, a keep-rows overlay covers
17789
+ * the grid and the pager shows a spinner; when there are no rows (first
17790
+ * load or the host dropped STN/SFN rows after a mode switch), the body shows
17729
17791
  * {@link LoadingStateComponent}. Distinct from {@link refreshing}.
17730
17792
  */
17731
17793
  loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
@@ -17974,7 +18036,18 @@ class TableComponent {
17974
18036
  '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
18037
  this.headerSoftClass() +
17976
18038
  ' ' +
17977
- this.stickySurfaceClass(col, 'head'));
18039
+ this.stickySurfaceClass(col, 'head') +
18040
+ (sticky ? '' : this.headerStickClass()));
18041
+ }
18042
+ /**
18043
+ * Vertical pin for scrolling header cells on desktop. Pinned left/right
18044
+ * headers get `lg:top-0` from {@link stickyPinClass} instead.
18045
+ */
18046
+ headerStickClass() {
18047
+ if (!this.stickyHeader()) {
18048
+ return '';
18049
+ }
18050
+ return ' lg:sticky lg:top-0 lg:z-[3] print:static';
17978
18051
  }
17979
18052
  /**
17980
18053
  * Body cell classes, including sticky pin when set.
@@ -18013,9 +18086,13 @@ class TableComponent {
18013
18086
  const side = edge === 'right' ? 'sticky right-0 z-[1]' : 'sticky left-0 z-[1]';
18014
18087
  if (surface === 'head') {
18015
18088
  // Fill comes from {@link headerSoftClass} on the cell — do not re-tint.
18016
- return `${side} z-[2]`;
18089
+ // Corner pins also stick vertically on desktop when stickyHeader is on.
18090
+ const vertical = this.stickyHeader()
18091
+ ? ' z-[2] lg:top-0 lg:z-[4] print:static'
18092
+ : ' z-[2]';
18093
+ return `${side}${vertical}`;
18017
18094
  }
18018
- const fill = 'bg-white dark:bg-ink-surface';
18095
+ const fill = 'bg-surface dark:bg-ink-surface';
18019
18096
  return `${side} ${fill}`;
18020
18097
  }
18021
18098
  /**
@@ -18121,7 +18198,7 @@ class TableComponent {
18121
18198
  this.sortChange.emit({ key, direction });
18122
18199
  }
18123
18200
  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: `
18201
+ 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
18202
  <div
18126
18203
  class="flex w-full min-w-0 flex-col gap-3"
18127
18204
  [attr.aria-busy]="loading() || refreshing() || null"
@@ -18187,19 +18264,19 @@ class TableComponent {
18187
18264
  }
18188
18265
 
18189
18266
  @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>
18267
+ <africanies-error-indicator
18268
+ class="w-full"
18269
+ [error]="staleMessage"
18270
+ retryText="Refresh"
18271
+ [refreshing]="refreshing()"
18272
+ (retry)="refreshClick.emit()"
18273
+ />
18199
18274
  }
18200
18275
 
18201
18276
  <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"
18277
+ 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"
18278
+ [class.lg:max-h-[min(70dvh,calc(100dvh-12rem))]]="stickyHeader()"
18279
+ [class.lg:overflow-auto]="stickyHeader()"
18203
18280
  >
18204
18281
  <table
18205
18282
  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 +18291,7 @@ class TableComponent {
18214
18291
  @if (someRowsExpanded()) {
18215
18292
  <button
18216
18293
  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"
18294
+ 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
18295
  aria-label="Collapse all"
18219
18296
  (click)="collapseAllRows()"
18220
18297
  >
@@ -18223,7 +18300,7 @@ class TableComponent {
18223
18300
  } @else {
18224
18301
  <button
18225
18302
  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"
18303
+ 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
18304
  aria-label="Expand all"
18228
18305
  [disabled]="rowList().length === 0"
18229
18306
  (click)="expandAllRows()"
@@ -18237,7 +18314,8 @@ class TableComponent {
18237
18314
  <th
18238
18315
  scope="col"
18239
18316
  [class]="headerCellClass(col)"
18240
- [style.width]="col.width ?? null"
18317
+ [style.width]="col.width ?? col.maxWidth ?? null"
18318
+ [style.max-width]="col.maxWidth ?? null"
18241
18319
  >
18242
18320
  @if (headerTemplateFor(col.key); as headerTpl) {
18243
18321
  <ng-container
@@ -18246,7 +18324,7 @@ class TableComponent {
18246
18324
  } @else if (col.sortable) {
18247
18325
  <button
18248
18326
  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"
18327
+ 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
18328
  (click)="onSortHeaderClick(col.key)"
18251
18329
  >
18252
18330
  <span>{{ col.header }}</span>
@@ -18302,7 +18380,7 @@ class TableComponent {
18302
18380
  <td [class]="expandBodyClass(i)">
18303
18381
  <button
18304
18382
  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"
18383
+ 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
18384
  [attr.aria-label]="
18307
18385
  isRowExpanded(row, i)
18308
18386
  ? 'Collapse row details'
@@ -18321,7 +18399,8 @@ class TableComponent {
18321
18399
  @for (col of columns(); track col.key) {
18322
18400
  <td
18323
18401
  [class]="bodyCellClass(col, i)"
18324
- [style.width]="col.width ?? null"
18402
+ [style.width]="col.width ?? col.maxWidth ?? null"
18403
+ [style.max-width]="col.maxWidth ?? null"
18325
18404
  >
18326
18405
  @if (templateFor(col.key); as tpl) {
18327
18406
  <ng-container
@@ -18351,7 +18430,7 @@ class TableComponent {
18351
18430
  [class.pointer-events-none]="!isRowExpanded(row, i)"
18352
18431
  >
18353
18432
  <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]"
18433
+ class="min-h-0 overflow-hidden border-t border-border-strong bg-surface-sunken dark:border-white/10 dark:bg-white/[0.05]"
18355
18434
  >
18356
18435
  <dl
18357
18436
  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 +18440,7 @@ class TableComponent {
18361
18440
  @for (detail of rowDetailDefs(); track detail.label()) {
18362
18441
  <div class="min-w-0">
18363
18442
  <dt
18364
- class="m-0 text-caption font-medium text-neutral-500 dark:text-neutral-400"
18443
+ class="m-0 text-caption font-medium text-neutral-600 dark:text-neutral-400"
18365
18444
  >
18366
18445
  {{ detail.label() }}
18367
18446
  </dt>
@@ -18386,6 +18465,18 @@ class TableComponent {
18386
18465
  }
18387
18466
  </tbody>
18388
18467
  </table>
18468
+ @if (paginationLoading()) {
18469
+ <div
18470
+ 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"
18471
+ data-testid="africanies-table-keep-rows-loading"
18472
+ aria-hidden="true"
18473
+ >
18474
+ <africanies-loading-state
18475
+ mode="inline"
18476
+ [message]="loadingLabel()"
18477
+ />
18478
+ </div>
18479
+ }
18389
18480
  </div>
18390
18481
 
18391
18482
  @if (meta(); as pager) {
@@ -18398,7 +18489,7 @@ class TableComponent {
18398
18489
  />
18399
18490
  }
18400
18491
  </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 });
18492
+ `, 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
18493
  }
18403
18494
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: TableComponent, decorators: [{
18404
18495
  type: Component,
@@ -18479,19 +18570,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18479
18570
  }
18480
18571
 
18481
18572
  @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>
18573
+ <africanies-error-indicator
18574
+ class="w-full"
18575
+ [error]="staleMessage"
18576
+ retryText="Refresh"
18577
+ [refreshing]="refreshing()"
18578
+ (retry)="refreshClick.emit()"
18579
+ />
18491
18580
  }
18492
18581
 
18493
18582
  <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"
18583
+ 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"
18584
+ [class.lg:max-h-[min(70dvh,calc(100dvh-12rem))]]="stickyHeader()"
18585
+ [class.lg:overflow-auto]="stickyHeader()"
18495
18586
  >
18496
18587
  <table
18497
18588
  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 +18597,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18506
18597
  @if (someRowsExpanded()) {
18507
18598
  <button
18508
18599
  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"
18600
+ 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
18601
  aria-label="Collapse all"
18511
18602
  (click)="collapseAllRows()"
18512
18603
  >
@@ -18515,7 +18606,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18515
18606
  } @else {
18516
18607
  <button
18517
18608
  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"
18609
+ 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
18610
  aria-label="Expand all"
18520
18611
  [disabled]="rowList().length === 0"
18521
18612
  (click)="expandAllRows()"
@@ -18529,7 +18620,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18529
18620
  <th
18530
18621
  scope="col"
18531
18622
  [class]="headerCellClass(col)"
18532
- [style.width]="col.width ?? null"
18623
+ [style.width]="col.width ?? col.maxWidth ?? null"
18624
+ [style.max-width]="col.maxWidth ?? null"
18533
18625
  >
18534
18626
  @if (headerTemplateFor(col.key); as headerTpl) {
18535
18627
  <ng-container
@@ -18538,7 +18630,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18538
18630
  } @else if (col.sortable) {
18539
18631
  <button
18540
18632
  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"
18633
+ 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
18634
  (click)="onSortHeaderClick(col.key)"
18543
18635
  >
18544
18636
  <span>{{ col.header }}</span>
@@ -18594,7 +18686,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18594
18686
  <td [class]="expandBodyClass(i)">
18595
18687
  <button
18596
18688
  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"
18689
+ 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
18690
  [attr.aria-label]="
18599
18691
  isRowExpanded(row, i)
18600
18692
  ? 'Collapse row details'
@@ -18613,7 +18705,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18613
18705
  @for (col of columns(); track col.key) {
18614
18706
  <td
18615
18707
  [class]="bodyCellClass(col, i)"
18616
- [style.width]="col.width ?? null"
18708
+ [style.width]="col.width ?? col.maxWidth ?? null"
18709
+ [style.max-width]="col.maxWidth ?? null"
18617
18710
  >
18618
18711
  @if (templateFor(col.key); as tpl) {
18619
18712
  <ng-container
@@ -18643,7 +18736,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18643
18736
  [class.pointer-events-none]="!isRowExpanded(row, i)"
18644
18737
  >
18645
18738
  <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]"
18739
+ class="min-h-0 overflow-hidden border-t border-border-strong bg-surface-sunken dark:border-white/10 dark:bg-white/[0.05]"
18647
18740
  >
18648
18741
  <dl
18649
18742
  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 +18746,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18653
18746
  @for (detail of rowDetailDefs(); track detail.label()) {
18654
18747
  <div class="min-w-0">
18655
18748
  <dt
18656
- class="m-0 text-caption font-medium text-neutral-500 dark:text-neutral-400"
18749
+ class="m-0 text-caption font-medium text-neutral-600 dark:text-neutral-400"
18657
18750
  >
18658
18751
  {{ detail.label() }}
18659
18752
  </dt>
@@ -18678,6 +18771,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18678
18771
  }
18679
18772
  </tbody>
18680
18773
  </table>
18774
+ @if (paginationLoading()) {
18775
+ <div
18776
+ 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"
18777
+ data-testid="africanies-table-keep-rows-loading"
18778
+ aria-hidden="true"
18779
+ >
18780
+ <africanies-loading-state
18781
+ mode="inline"
18782
+ [message]="loadingLabel()"
18783
+ />
18784
+ </div>
18785
+ }
18681
18786
  </div>
18682
18787
 
18683
18788
  @if (meta(); as pager) {
@@ -18690,8 +18795,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18690
18795
  />
18691
18796
  }
18692
18797
  </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 }] }] } });
18798
+ `, styles: [":host tr.africanies-table-row:hover>td{background-color:#e9eef5}:host-context(.dark) tr.africanies-table-row:hover>td{background-color:#272729}\n"] }]
18799
+ }], 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
18800
 
18696
18801
  /**
18697
18802
  * Presentational table with template-based cells.
@@ -18837,7 +18942,7 @@ class StepperComponent {
18837
18942
  if (past) {
18838
18943
  return 'cursor-pointer text-ink hover:bg-background-welcome dark:text-white dark:hover:bg-white/10';
18839
18944
  }
18840
- return 'opacity-40 text-neutral-500 dark:text-neutral-400';
18945
+ return 'opacity-40 text-neutral-600 dark:text-neutral-400';
18841
18946
  }
18842
18947
  stepBadgeClass(index) {
18843
18948
  const base = 'inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full border text-body-sm font-medium';
@@ -18845,7 +18950,7 @@ class StepperComponent {
18845
18950
  const c = this.modeColor.classes();
18846
18951
  return `${base} ${c.border} ${c.bg} text-white`;
18847
18952
  }
18848
- return `${base} border-border bg-white text-neutral-500 dark:border-white/20 dark:bg-ink-950 dark:text-neutral-400`;
18953
+ return `${base} border-control bg-surface text-neutral-600 dark:border-white/20 dark:bg-ink-950 dark:text-neutral-400`;
18849
18954
  }
18850
18955
  canActivate(index) {
18851
18956
  if (index === this.activeIndex()) {
@@ -18870,7 +18975,7 @@ class StepperComponent {
18870
18975
  <div class="flex flex-col gap-5 text-ink dark:text-white">
18871
18976
  <div class="flex flex-col gap-2">
18872
18977
  <p
18873
- class="m-0 text-caption font-medium uppercase tracking-wide text-neutral-500 dark:text-neutral-400"
18978
+ class="m-0 text-caption font-medium uppercase tracking-wide text-neutral-600 dark:text-neutral-400"
18874
18979
  >
18875
18980
  Step {{ activeIndex() + 1 }} of {{ steps().length }}
18876
18981
  <span
@@ -18899,7 +19004,7 @@ class StepperComponent {
18899
19004
  <li class="flex min-w-0 shrink-0 items-center gap-1">
18900
19005
  <button
18901
19006
  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"
19007
+ 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
19008
  [class]="headerButtonClass(i)"
18904
19009
  [attr.aria-current]="i === activeIndex() ? 'step' : null"
18905
19010
  [disabled]="!canActivate(i)"
@@ -18938,7 +19043,7 @@ class StepperComponent {
18938
19043
  }
18939
19044
  </div>
18940
19045
  </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 });
19046
+ `, 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
19047
  }
18943
19048
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: StepperComponent, decorators: [{
18944
19049
  type: Component,
@@ -18946,7 +19051,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18946
19051
  <div class="flex flex-col gap-5 text-ink dark:text-white">
18947
19052
  <div class="flex flex-col gap-2">
18948
19053
  <p
18949
- class="m-0 text-caption font-medium uppercase tracking-wide text-neutral-500 dark:text-neutral-400"
19054
+ class="m-0 text-caption font-medium uppercase tracking-wide text-neutral-600 dark:text-neutral-400"
18950
19055
  >
18951
19056
  Step {{ activeIndex() + 1 }} of {{ steps().length }}
18952
19057
  <span
@@ -18975,7 +19080,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18975
19080
  <li class="flex min-w-0 shrink-0 items-center gap-1">
18976
19081
  <button
18977
19082
  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"
19083
+ 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
19084
  [class]="headerButtonClass(i)"
18980
19085
  [attr.aria-current]="i === activeIndex() ? 'step' : null"
18981
19086
  [disabled]="!canActivate(i)"
@@ -19014,7 +19119,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
19014
19119
  }
19015
19120
  </div>
19016
19121
  </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"] }]
19122
+ `, 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
19123
  }], 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
19124
 
19020
19125
  /**