@africanies/angular-web-sdk 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/.nx/cache/{17491371155400585077 → 13466509747676776487}/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +139 -51
  2. package/.nx/cache/13466509747676776487/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +1 -0
  3. package/.nx/cache/{17491371155400585077 → 13466509747676776487}/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +14 -11
  4. package/.nx/cache/{17499961385238663835 → 14995846848874973576}/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +83 -63
  5. package/.nx/cache/14995846848874973576/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -0
  6. package/.nx/cache/{17499961385238663835 → 14995846848874973576}/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +22 -19
  7. package/.nx/cache/{12276756491762927325 → 15302120112746851896}/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +34 -6
  8. package/.nx/cache/15302120112746851896/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -0
  9. package/.nx/cache/{12276756491762927325 → 15302120112746851896}/dist/libs/africanies-theme/tailwind-preset.cjs +104 -19
  10. package/.nx/cache/{12276756491762927325 → 15302120112746851896}/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +21 -2
  11. package/.nx/cache/18393832731522001121/dist/libs/africanies-core/README.md +85 -0
  12. package/.nx/cache/18393832731522001121/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +4634 -0
  13. package/.nx/cache/18393832731522001121/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +1 -0
  14. package/.nx/cache/18393832731522001121/dist/libs/africanies-core/package.json +31 -0
  15. package/.nx/cache/18393832731522001121/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +2743 -0
  16. package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/README.md +69 -0
  17. package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/assets/icons.sprite.svg +1851 -0
  18. package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs +833 -0
  19. package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs.map +1 -0
  20. package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/package.json +30 -0
  21. package/.nx/cache/5151121439688073678/dist/libs/africanies-icons/types/africanies-africanies-icons.d.ts +108 -0
  22. package/.nx/cache/{4082520855419352840 → 6673686859704879504}/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +12 -2
  23. package/.nx/cache/6673686859704879504/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -0
  24. package/.nx/cache/{4082520855419352840 → 6673686859704879504}/dist/libs/africanies-theme/tailwind-preset.cjs +16 -7
  25. package/.nx/cache/{4082520855419352840 → 6673686859704879504}/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +11 -3
  26. package/.nx/cache/877026836685850193/dist/libs/africanies-models/README.md +60 -0
  27. package/.nx/cache/877026836685850193/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs +613 -0
  28. package/.nx/cache/877026836685850193/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs.map +1 -0
  29. package/.nx/cache/877026836685850193/dist/libs/africanies-models/package.json +23 -0
  30. package/.nx/cache/877026836685850193/dist/libs/africanies-models/types/africanies-africanies-models.d.ts +1452 -0
  31. package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +866 -632
  32. package/.nx/cache/9406544675747598148/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -0
  33. package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +170 -130
  34. package/.nx/cache/run.json +42 -42
  35. package/.nx/cache/terminalOutputs/{17491371155400585077 → 13466509747676776487} +1 -1
  36. package/.nx/cache/terminalOutputs/14995846848874973576 +21 -0
  37. package/.nx/cache/terminalOutputs/{3017748574492080228 → 15149466893522345489} +16 -7
  38. package/.nx/cache/terminalOutputs/{4082520855419352840 → 15302120112746851896} +1 -1
  39. package/.nx/cache/terminalOutputs/{8648260256613557249 → 15835338512221847842} +18 -8
  40. package/.nx/cache/terminalOutputs/17960163310445607219 +6 -0
  41. package/.nx/cache/terminalOutputs/18393832731522001121 +21 -0
  42. package/.nx/cache/terminalOutputs/{3314955264390318442 → 5151121439688073678} +6 -6
  43. package/.nx/cache/terminalOutputs/{12276756491762927325 → 6673686859704879504} +2 -2
  44. package/.nx/cache/terminalOutputs/7462873189468732296 +17 -0
  45. package/.nx/cache/terminalOutputs/8032148447904822353 +185 -0
  46. package/.nx/cache/terminalOutputs/877026836685850193 +21 -0
  47. package/.nx/cache/terminalOutputs/9406544675747598148 +21 -0
  48. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db +0 -0
  49. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-shm +0 -0
  50. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-wal +0 -0
  51. package/.nx/workspace-data/d/daemon.log +3962 -0
  52. package/.nx/workspace-data/d/server-process.json +2 -2
  53. package/.nx/workspace-data/file-map.json +3102 -3036
  54. package/.nx/workspace-data/nx_files.nxt +0 -0
  55. package/.nx/workspace-data/project-graph.json +7 -2
  56. package/apps/playground/src/app/pages/button-page.ts +5 -3
  57. package/apps/playground/src/app/pages/feedback-page.ts +1 -1
  58. package/apps/playground/src/app/pages/home-page.ts +1 -1
  59. package/apps/playground/src/app/pages/icons-page.ts +3 -3
  60. package/apps/playground/src/app/pages/lecture-page.ts +3 -3
  61. package/apps/playground/src/app/pages/models-page.ts +1 -1
  62. package/apps/playground/src/app/pages/overlay-demos.ts +1 -1
  63. package/apps/playground/src/app/pages/tokens-page.ts +29 -11
  64. package/apps/playground/src/app/pages/usecases/onboarding-login.page.ts +1 -1
  65. package/apps/playground/src/app/pages/usecases/onboarding-reset-password.page.ts +1 -1
  66. package/apps/playground/src/app/pages/usecases/shipment-detail.page.ts +1 -1
  67. package/apps/playground/src/app/pages/viewport-preview.component.ts +1 -1
  68. package/apps/playground/src/app/snippets/button.snippets.ts +4 -3
  69. package/apps/playground/src/app/snippets/filters.snippets.ts +10 -0
  70. package/apps/playground/src/index.html +6 -0
  71. package/apps/playground/src/styles.css +4 -5
  72. package/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +131 -8
  73. package/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +1 -1
  74. package/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +8 -6
  75. package/dist/libs/africanies-icons/assets/icons.sprite.svg +7 -0
  76. package/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs +1 -0
  77. package/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs.map +1 -1
  78. package/dist/libs/africanies-icons/types/africanies-africanies-icons.d.ts +2 -2
  79. package/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs +22 -8
  80. package/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs.map +1 -1
  81. package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +30 -8
  82. package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -1
  83. package/dist/libs/africanies-theme/tailwind-preset.cjs +89 -12
  84. package/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +14 -4
  85. package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +765 -615
  86. package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -1
  87. package/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +148 -115
  88. package/libs/africanies-core/src/index.ts +1 -0
  89. package/libs/africanies-core/src/lib/notification/index.ts +1 -1
  90. package/libs/africanies-core/src/lib/notification/notification-link.spec.ts +40 -7
  91. package/libs/africanies-core/src/lib/notification/notification-link.ts +143 -7
  92. package/libs/africanies-icons/src/assets/icons.sprite.svg +7 -0
  93. package/libs/africanies-icons/src/lib/icon-name.ts +1 -0
  94. package/libs/africanies-models/src/lib/filters/configs/seed-configs.ts +22 -8
  95. package/libs/africanies-theme/src/lib/mode-color.safelist.ts +20 -2
  96. package/libs/africanies-theme/src/lib/mode-color.service.spec.ts +55 -0
  97. package/libs/africanies-theme/src/lib/mode-color.service.ts +23 -7
  98. package/libs/africanies-theme/tailwind-preset.cjs +89 -12
  99. package/libs/africanies-ui/src/lib/action-menu/action-menu.component.ts +3 -3
  100. package/libs/africanies-ui/src/lib/alert/alert.component.ts +4 -4
  101. package/libs/africanies-ui/src/lib/avatar/avatar-menu.component.ts +2 -2
  102. package/libs/africanies-ui/src/lib/button/button.component.spec.ts +7 -7
  103. package/libs/africanies-ui/src/lib/button/button.component.ts +10 -6
  104. package/libs/africanies-ui/src/lib/chip/chip.component.ts +4 -4
  105. package/libs/africanies-ui/src/lib/content-stack/content-stack.component.ts +1 -1
  106. package/libs/africanies-ui/src/lib/feedback/empty-state.component.ts +1 -1
  107. package/libs/africanies-ui/src/lib/feedback/error-indicator.component.ts +3 -3
  108. package/libs/africanies-ui/src/lib/feedback/error-state.component.ts +3 -3
  109. package/libs/africanies-ui/src/lib/filters/filter-drawer.panel.ts +44 -27
  110. package/libs/africanies-ui/src/lib/forms/checkbox/checkbox.component.ts +2 -1
  111. package/libs/africanies-ui/src/lib/forms/file-upload/file-preview.dialog.ts +2 -2
  112. package/libs/africanies-ui/src/lib/forms/file-upload/file-upload.component.ts +5 -5
  113. package/libs/africanies-ui/src/lib/forms/form-field.classes.ts +37 -7
  114. package/libs/africanies-ui/src/lib/forms/otp-input/otp-input.component.ts +2 -1
  115. package/libs/africanies-ui/src/lib/forms/radio/radio.component.ts +2 -1
  116. package/libs/africanies-ui/src/lib/forms/select/select.component.ts +2 -2
  117. package/libs/africanies-ui/src/lib/forms/toggle/toggle.component.ts +1 -1
  118. package/libs/africanies-ui/src/lib/image/image.component.ts +1 -1
  119. package/libs/africanies-ui/src/lib/info-popover/info-popover.component.ts +2 -2
  120. package/libs/africanies-ui/src/lib/layout/app-shell-content-header.component.ts +1 -1
  121. package/libs/africanies-ui/src/lib/layout/app-shell-header.component.ts +6 -6
  122. package/libs/africanies-ui/src/lib/layout/app-shell.component.html +2 -2
  123. package/libs/africanies-ui/src/lib/layout/app-shell.component.ts +5 -5
  124. package/libs/africanies-ui/src/lib/navigation/shipping-mode-switch/shipping-mode-switch.component.ts +4 -4
  125. package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.html +21 -13
  126. package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.ts +23 -13
  127. package/libs/africanies-ui/src/lib/notifications/notification-drawer.panel.ts +27 -14
  128. package/libs/africanies-ui/src/lib/overlay/confirm-dialog.component.ts +2 -2
  129. package/libs/africanies-ui/src/lib/overlay/drawer.service.ts +3 -3
  130. package/libs/africanies-ui/src/lib/overlay/modal.service.ts +4 -4
  131. package/libs/africanies-ui/src/lib/overlay/overlay-frame.component.ts +1 -1
  132. package/libs/africanies-ui/src/lib/pagination/pagination.component.ts +4 -4
  133. package/libs/africanies-ui/src/lib/stepper/stepper.component.ts +7 -7
  134. package/libs/africanies-ui/src/lib/table/table-column.ts +9 -0
  135. package/libs/africanies-ui/src/lib/table/table.component.spec.ts +99 -0
  136. package/libs/africanies-ui/src/lib/table/table.component.ts +75 -33
  137. package/libs/africanies-ui/src/lib/toast/toast-host.component.ts +4 -4
  138. package/libs/africanies-ui/src/lib/toast/toast-item.component.ts +7 -7
  139. package/package.json +1 -1
  140. package/svg/hand-coins.svg +9 -0
  141. package/.nx/cache/12276756491762927325/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +0 -1
  142. package/.nx/cache/17491371155400585077/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +0 -1
  143. package/.nx/cache/17499961385238663835/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +0 -1
  144. package/.nx/cache/3314955264390318442/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +0 -1
  145. package/.nx/cache/4082520855419352840/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +0 -1
  146. package/.nx/cache/terminalOutputs/17499961385238663835 +0 -21
  147. /package/.nx/cache/{17491371155400585077 → 13466509747676776487}/dist/libs/africanies-core/README.md +0 -0
  148. /package/.nx/cache/{17491371155400585077 → 13466509747676776487}/dist/libs/africanies-core/package.json +0 -0
  149. /package/.nx/cache/{17499961385238663835 → 14995846848874973576}/dist/libs/africanies-ui/README.md +0 -0
  150. /package/.nx/cache/{17499961385238663835 → 14995846848874973576}/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
  151. /package/.nx/cache/{17499961385238663835 → 14995846848874973576}/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
  152. /package/.nx/cache/{17499961385238663835 → 14995846848874973576}/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +0 -0
  153. /package/.nx/cache/{17499961385238663835 → 14995846848874973576}/dist/libs/africanies-ui/assets/carriers/dhl.svg +0 -0
  154. /package/.nx/cache/{17499961385238663835 → 14995846848874973576}/dist/libs/africanies-ui/package.json +0 -0
  155. /package/.nx/cache/{12276756491762927325 → 15302120112746851896}/dist/libs/africanies-theme/README.md +0 -0
  156. /package/.nx/cache/{12276756491762927325 → 15302120112746851896}/dist/libs/africanies-theme/package.json +0 -0
  157. /package/.nx/cache/{4082520855419352840 → 6673686859704879504}/dist/libs/africanies-theme/README.md +0 -0
  158. /package/.nx/cache/{4082520855419352840 → 6673686859704879504}/dist/libs/africanies-theme/package.json +0 -0
  159. /package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/README.md +0 -0
  160. /package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
  161. /package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
  162. /package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +0 -0
  163. /package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/assets/carriers/dhl.svg +0 -0
  164. /package/.nx/cache/{3314955264390318442 → 9406544675747598148}/dist/libs/africanies-ui/package.json +0 -0
  165. /package/.nx/cache/terminalOutputs/{16279119293212733518 → 14005735098959303198} +0 -0
  166. /package/.nx/cache/terminalOutputs/{7126777490381568487 → 1446890025183346611} +0 -0
  167. /package/.nx/cache/terminalOutputs/{9408529484792607601 → 6337357866566791975} +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',
@@ -108,16 +108,19 @@ class ButtonComponent {
108
108
  variantClass = this.modeColor.classes().ghostPrimary;
109
109
  }
110
110
  else if (variant === 'secondary') {
111
+ variantClass = this.modeColor.classes().stroked;
112
+ }
113
+ else if (variant === 'flat') {
111
114
  variantClass =
112
- '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';
113
116
  }
114
117
  else if (variant === 'ghost') {
115
118
  variantClass =
116
- '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';
117
120
  }
118
121
  else if (variant === 'ghost-danger') {
119
122
  variantClass =
120
- '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';
121
124
  }
122
125
  else if (variant === 'underline') {
123
126
  variantClass =
@@ -125,7 +128,7 @@ class ButtonComponent {
125
128
  }
126
129
  else {
127
130
  variantClass =
128
- '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';
129
132
  }
130
133
  return `${base} ${sizes[this.size()]} ${variantClass}`;
131
134
  }, /* @ts-ignore */
@@ -268,20 +271,20 @@ class ToastItemComponent {
268
271
  return v === 'warning' || v === 'danger' ? 'assertive' : 'polite';
269
272
  }, /* @ts-ignore */
270
273
  ...(ngDevMode ? [{ debugName: "a11yLive" }] : /* istanbul ignore next */ []));
271
- shellClass = computed(() => 'africanies-toast-enter relative flex w-full overflow-hidden rounded-lg border border-border ' +
272
- 'bg-white/95 text-ink shadow-lg backdrop-blur-md ' +
273
- 'dark:border-white/15 dark:bg-ink/95 dark:text-white', /* @ts-ignore */
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
+ 'dark:border-white/15 dark:bg-ink-950/95 dark:text-white', /* @ts-ignore */
274
277
  ...(ngDevMode ? [{ debugName: "shellClass" }] : /* istanbul ignore next */ []));
275
278
  accentClass = computed(() => `w-1 shrink-0 self-stretch ${accentFill(this.item().variant)}`, /* @ts-ignore */
276
279
  ...(ngDevMode ? [{ debugName: "accentClass" }] : /* istanbul ignore next */ []));
277
280
  iconClass = computed(() => {
278
281
  switch (this.item().variant) {
279
282
  case 'success':
280
- return 'text-export dark:text-export-light';
283
+ return 'text-export-strong dark:text-export-light';
281
284
  case 'warning':
282
285
  return 'text-warning-dark dark:text-warning';
283
286
  case 'danger':
284
- return 'text-danger';
287
+ return 'text-danger-dark dark:text-danger';
285
288
  default:
286
289
  return 'text-ink-blue dark:text-neutral-300';
287
290
  }
@@ -291,11 +294,11 @@ class ToastItemComponent {
291
294
  const base = 'm-0 break-words text-body-sm font-semibold';
292
295
  switch (this.item().variant) {
293
296
  case 'success':
294
- return `${base} text-export dark:text-export-light`;
297
+ return `${base} text-export-strong dark:text-export-light`;
295
298
  case 'warning':
296
299
  return `${base} text-warning-dark dark:text-warning`;
297
300
  case 'danger':
298
- return `${base} text-danger`;
301
+ return `${base} text-danger-dark dark:text-danger`;
299
302
  default:
300
303
  return `${base} text-ink dark:text-white`;
301
304
  }
@@ -681,14 +684,14 @@ class ToastHostComponent {
681
684
  <div class="flex w-full flex-col gap-2">
682
685
  @if (toastService.showHostActions()) {
683
686
  <div
684
- class="sticky top-0 z-10 flex flex-wrap justify-end gap-2 bg-white/80 pb-1 backdrop-blur-sm dark:bg-ink/80"
687
+ class="sticky top-0 z-10 flex flex-wrap justify-end gap-2 bg-white/80 pb-1 backdrop-blur-sm dark:bg-ink-surface/80"
685
688
  >
686
689
  @if (toastService.hasStacks()) {
687
690
  @if (toastService.allStacksExpanded()) {
688
691
  <button
689
692
  africanies-button
690
693
  type="button"
691
- variant="secondary"
694
+ variant="flat"
692
695
  size="sm"
693
696
  class="!min-h-0 !rounded-full !bg-white !px-2.5 !py-1 !text-caption dark:!bg-ink"
694
697
  (click)="toastService.collapseAll()"
@@ -699,7 +702,7 @@ class ToastHostComponent {
699
702
  <button
700
703
  africanies-button
701
704
  type="button"
702
- variant="secondary"
705
+ variant="flat"
703
706
  size="sm"
704
707
  class="!min-h-0 !rounded-full !bg-white !px-2.5 !py-1 !text-caption dark:!bg-ink"
705
708
  (click)="toastService.expandAll()"
@@ -711,7 +714,7 @@ class ToastHostComponent {
711
714
  <button
712
715
  africanies-button
713
716
  type="button"
714
- variant="secondary"
717
+ variant="flat"
715
718
  size="sm"
716
719
  class="!min-h-0 !rounded-full !bg-white !px-2.5 !py-1 !text-caption dark:!bg-ink"
717
720
  (click)="toastService.clear()"
@@ -750,14 +753,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
750
753
  <div class="flex w-full flex-col gap-2">
751
754
  @if (toastService.showHostActions()) {
752
755
  <div
753
- class="sticky top-0 z-10 flex flex-wrap justify-end gap-2 bg-white/80 pb-1 backdrop-blur-sm dark:bg-ink/80"
756
+ class="sticky top-0 z-10 flex flex-wrap justify-end gap-2 bg-white/80 pb-1 backdrop-blur-sm dark:bg-ink-surface/80"
754
757
  >
755
758
  @if (toastService.hasStacks()) {
756
759
  @if (toastService.allStacksExpanded()) {
757
760
  <button
758
761
  africanies-button
759
762
  type="button"
760
- variant="secondary"
763
+ variant="flat"
761
764
  size="sm"
762
765
  class="!min-h-0 !rounded-full !bg-white !px-2.5 !py-1 !text-caption dark:!bg-ink"
763
766
  (click)="toastService.collapseAll()"
@@ -768,7 +771,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
768
771
  <button
769
772
  africanies-button
770
773
  type="button"
771
- variant="secondary"
774
+ variant="flat"
772
775
  size="sm"
773
776
  class="!min-h-0 !rounded-full !bg-white !px-2.5 !py-1 !text-caption dark:!bg-ink"
774
777
  (click)="toastService.expandAll()"
@@ -780,7 +783,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
780
783
  <button
781
784
  africanies-button
782
785
  type="button"
783
- variant="secondary"
786
+ variant="flat"
784
787
  size="sm"
785
788
  class="!min-h-0 !rounded-full !bg-white !px-2.5 !py-1 !text-caption dark:!bg-ink"
786
789
  (click)="toastService.clear()"
@@ -1501,9 +1504,9 @@ class ActionMenuComponent {
1501
1504
  const active = this.activeIndex() === index;
1502
1505
  const base = 'flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-body-sm no-underline transition-colors ' +
1503
1506
  'disabled:cursor-not-allowed disabled:opacity-50 ' +
1504
- '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';
1505
1508
  if (item.danger) {
1506
- return (`${base} text-danger ` +
1509
+ return (`${base} text-danger-dark dark:text-danger ` +
1507
1510
  (active
1508
1511
  ? 'bg-danger-subtle dark:bg-danger/15'
1509
1512
  : 'hover:bg-danger-subtle dark:hover:bg-danger/15'));
@@ -1707,7 +1710,7 @@ class ActionMenuComponent {
1707
1710
  >
1708
1711
  <div
1709
1712
  #panel
1710
- 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"
1711
1714
  role="menu"
1712
1715
  tabindex="-1"
1713
1716
  [attr.aria-label]="ariaLabel()"
@@ -1824,7 +1827,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
1824
1827
  >
1825
1828
  <div
1826
1829
  #panel
1827
- 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"
1828
1831
  role="menu"
1829
1832
  tabindex="-1"
1830
1833
  [attr.aria-label]="ariaLabel()"
@@ -1927,7 +1930,7 @@ class EmptyStateComponent {
1927
1930
  <button
1928
1931
  africanies-button
1929
1932
  type="button"
1930
- variant="secondary"
1933
+ variant="flat"
1931
1934
  [disabled]="refreshing()"
1932
1935
  (click)="retry.emit()"
1933
1936
  >
@@ -1958,7 +1961,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
1958
1961
  <button
1959
1962
  africanies-button
1960
1963
  type="button"
1961
- variant="secondary"
1964
+ variant="flat"
1962
1965
  [disabled]="refreshing()"
1963
1966
  (click)="retry.emit()"
1964
1967
  >
@@ -2053,10 +2056,10 @@ class ErrorIndicatorComponent {
2053
2056
  aria-hidden="true"
2054
2057
  >
2055
2058
  <span
2056
- 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"
2057
2060
  ></span>
2058
2061
  <span
2059
- 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"
2060
2063
  ></span>
2061
2064
  </span>
2062
2065
 
@@ -2071,7 +2074,7 @@ class ErrorIndicatorComponent {
2071
2074
  type="button"
2072
2075
  variant="ghost"
2073
2076
  size="sm"
2074
- 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"
2075
2078
  [class.opacity-50]="disabled()"
2076
2079
  [disabled]="disabled()"
2077
2080
  (click)="onRetry()"
@@ -2098,10 +2101,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2098
2101
  aria-hidden="true"
2099
2102
  >
2100
2103
  <span
2101
- 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"
2102
2105
  ></span>
2103
2106
  <span
2104
- 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"
2105
2108
  ></span>
2106
2109
  </span>
2107
2110
 
@@ -2116,7 +2119,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2116
2119
  type="button"
2117
2120
  variant="ghost"
2118
2121
  size="sm"
2119
- 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"
2120
2123
  [class.opacity-50]="disabled()"
2121
2124
  [disabled]="disabled()"
2122
2125
  (click)="onRetry()"
@@ -2167,12 +2170,12 @@ class ErrorStateComponent {
2167
2170
  class="flex flex-col items-center justify-center gap-3 py-10 px-4 text-center text-ink dark:text-white"
2168
2171
  role="alert"
2169
2172
  >
2170
- <africanies-icon name="warning" [size]="32" class="text-danger" />
2171
- <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>
2172
2175
  <button
2173
2176
  africanies-button
2174
2177
  type="button"
2175
- variant="secondary"
2178
+ variant="flat"
2176
2179
  [disabled]="refreshing()"
2177
2180
  (click)="retry.emit()"
2178
2181
  >
@@ -2198,12 +2201,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2198
2201
  class="flex flex-col items-center justify-center gap-3 py-10 px-4 text-center text-ink dark:text-white"
2199
2202
  role="alert"
2200
2203
  >
2201
- <africanies-icon name="warning" [size]="32" class="text-danger" />
2202
- <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>
2203
2206
  <button
2204
2207
  africanies-button
2205
2208
  type="button"
2206
- variant="secondary"
2209
+ variant="flat"
2207
2210
  [disabled]="refreshing()"
2208
2211
  (click)="retry.emit()"
2209
2212
  >
@@ -2736,11 +2739,11 @@ class AlertComponent {
2736
2739
  iconClass = computed(() => {
2737
2740
  switch (this.variant()) {
2738
2741
  case 'success':
2739
- return 'text-export dark:text-export-light';
2742
+ return 'text-export-strong dark:text-export-light';
2740
2743
  case 'warning':
2741
2744
  return 'text-warning-dark dark:text-warning';
2742
2745
  case 'danger':
2743
- return 'text-danger';
2746
+ return 'text-danger-dark dark:text-danger';
2744
2747
  default:
2745
2748
  return 'text-ink-blue dark:text-neutral-300';
2746
2749
  }
@@ -2750,11 +2753,11 @@ class AlertComponent {
2750
2753
  const base = 'm-0 text-body font-medium';
2751
2754
  switch (this.variant()) {
2752
2755
  case 'success':
2753
- return `${base} text-export dark:text-export-light`;
2756
+ return `${base} text-export-strong dark:text-export-light`;
2754
2757
  case 'warning':
2755
2758
  return `${base} text-warning-dark dark:text-warning`;
2756
2759
  case 'danger':
2757
- return `${base} text-danger`;
2760
+ return `${base} text-danger-dark dark:text-danger`;
2758
2761
  default:
2759
2762
  return `${base} text-ink dark:text-white`;
2760
2763
  }
@@ -2907,13 +2910,13 @@ class ChipComponent {
2907
2910
  switch (this.variant()) {
2908
2911
  case 'success':
2909
2912
  case 'export':
2910
- 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';
2911
2914
  case 'import':
2912
- 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';
2913
2916
  case 'warning':
2914
2917
  return 'bg-warning-subtle text-warning-dark dark:bg-warning/15 dark:text-warning';
2915
2918
  case 'danger':
2916
- 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';
2917
2920
  default:
2918
2921
  return 'bg-border/60 text-ink dark:bg-white/10 dark:text-neutral-200';
2919
2922
  }
@@ -2934,7 +2937,7 @@ class ChipComponent {
2934
2937
  @if (removable()) {
2935
2938
  <button
2936
2939
  type="button"
2937
- 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"
2938
2941
  [attr.aria-label]="removeLabel()"
2939
2942
  (click)="onRemove($event)"
2940
2943
  >
@@ -2962,7 +2965,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2962
2965
  @if (removable()) {
2963
2966
  <button
2964
2967
  type="button"
2965
- 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"
2966
2969
  [attr.aria-label]="removeLabel()"
2967
2970
  (click)="onRemove($event)"
2968
2971
  >
@@ -3177,7 +3180,7 @@ class ContentStackComponent {
3177
3180
  </p>
3178
3181
  }
3179
3182
  @if (extraLine()) {
3180
- <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">
3181
3184
  {{ extraLine() }}
3182
3185
  </p>
3183
3186
  }
@@ -3206,7 +3209,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
3206
3209
  </p>
3207
3210
  }
3208
3211
  @if (extraLine()) {
3209
- <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">
3210
3213
  {{ extraLine() }}
3211
3214
  </p>
3212
3215
  }
@@ -3779,8 +3782,8 @@ class InfoPopoverComponent {
3779
3782
  * Bound via `[class]` so arrow position tracks preferred placement.
3780
3783
  */
3781
3784
  panelArrowClass = computed(() => {
3782
- 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';
3783
- 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";
3784
3787
  switch (this.placement()) {
3785
3788
  case 'bottom':
3786
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`;
@@ -4080,22 +4083,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
4080
4083
  * scanner must see full utility names in this package so they survive purge
4081
4084
  * when consumers scan the published UI bundle.
4082
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';
4083
4100
  /** Label above the control. Hidden when empty so callers can omit a label. */
4084
4101
  const FORM_LABEL_CLASS = 'block text-body-sm font-medium text-ink dark:text-white mb-1.5 empty:hidden';
4085
- /** Default control shell — `h-10` matches md `africanies-button`. */
4086
- const FORM_FIELD_CLASS = 'flex items-center w-full h-10 box-border rounded-md border border-neutral-300 bg-white dark:bg-ink-950 text-ink dark:text-white dark:border-white/25 ' +
4087
- 'focus-within:outline focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-ink';
4088
4102
  /**
4089
- * Error border`!` so it wins over shell `border-neutral-300` /
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';
4110
+ /**
4111
+ * Error border — `!` so it wins over shell `border-border` /
4090
4112
  * `dark:border-white/25` (same specificity; those utilities sort later in the
4091
4113
  * generated CSS and otherwise paint over `border-danger`).
4092
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
+ *
4093
4118
  * Keep these literals static for Tailwind content scanning in consumer apps.
4094
4119
  */
4095
- 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';
4096
4121
  /** Native input / textarea inside a shell. */
4097
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 ' +
4098
- 'placeholder:text-neutral-400 disabled:cursor-not-allowed';
4123
+ 'placeholder:text-neutral-500 disabled:cursor-not-allowed';
4099
4124
  /** Prefix / suffix projection gutters. Hidden when nothing is projected. */
4100
4125
  const FORM_AFFIX_CLASS = 'inline-flex items-center shrink-0 px-2.5 text-neutral-600 dark:text-neutral-400 empty:hidden';
4101
4126
  /**
@@ -4110,7 +4135,7 @@ const FORM_DATE_INNER_CLASS = FORM_CONTROL_INNER_CLASS +
4110
4135
  ' cursor-pointer [&::-webkit-calendar-picker-indicator]:cursor-pointer';
4111
4136
  /** Hint under the control (hidden when error is shown). */
4112
4137
  const FORM_HINT_CLASS = 'mt-1.5 text-caption text-neutral-600 dark:text-neutral-400 m-0';
4113
- /** Field-level validation message (not ErrorStateComponent). */
4138
+ /** Field-level validation message (not ErrorStateComponent). Matches `#ff001c`. */
4114
4139
  const FORM_ERROR_CLASS = 'mt-1.5 text-caption text-danger m-0';
4115
4140
  /** Disabled shell — dimmed with not-allowed cursor over the full field chrome. */
4116
4141
  const FORM_DISABLED_CLASS = 'opacity-50 cursor-not-allowed';
@@ -4341,229 +4366,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
4341
4366
  }]
4342
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 }] }] } });
4343
4368
 
4344
- let nextRadioId = 0;
4345
- /**
4346
- * Radio group following the shared AFRICANIES form field pattern.
4347
- *
4348
- * **Prefix/suffix slots are omitted** — radios are mutually exclusive options
4349
- * with labels; affix projection does not apply.
4350
- *
4351
- * Selected ring and center disc follow {@link ModeColorService}
4352
- * (SFN green / STN orange).
4353
- *
4354
- * @typeParam T - Option value type.
4355
- *
4356
- * @example
4357
- * ```html
4358
- * <africanies-radio
4359
- * label="Shipment mode"
4360
- * [options]="modeOptions"
4361
- * [(value)]="mode"
4362
- * />
4363
- * ```
4364
- */
4365
- class RadioComponent {
4366
- modeColor = inject(ModeColorService);
4367
- groupName = `africanies-radio-${++nextRadioId}`;
4368
- hintId = `${this.groupName}-hint`;
4369
- errorId = `${this.groupName}-error`;
4370
- labelClass = FORM_LABEL_CLASS;
4371
- hintClass = FORM_HINT_CLASS;
4372
- errorClass = FORM_ERROR_CLASS;
4373
- /** Group legend / label. */
4374
- label = input('', /* @ts-ignore */
4375
- ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
4376
- /** Helper copy. Hidden while {@link error} is set. */
4377
- hint = input(undefined, /* @ts-ignore */
4378
- ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
4379
- /** Field-level validation message. Distinct from `ErrorStateComponent`. */
4380
- error = input(null, /* @ts-ignore */
4381
- ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
4382
- /** Options rendered as radios. */
4383
- options = input([], /* @ts-ignore */
4384
- ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
4385
- /** Selected option value (`[(value)]` + CVA). */
4386
- value = model(null, /* @ts-ignore */
4387
- ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
4388
- /** Host disable flag (in addition to CVA). */
4389
- disabledInput = input(false, { ...(ngDevMode ? { debugName: "disabledInput" } : /* istanbul ignore next */ {}), alias: 'disabled',
4390
- transform: booleanAttribute });
4391
- cvaDisabled = signal(false, /* @ts-ignore */
4392
- ...(ngDevMode ? [{ debugName: "cvaDisabled" }] : /* istanbul ignore next */ []));
4393
- disabled = computed(() => this.disabledInput() || this.cvaDisabled(), /* @ts-ignore */
4394
- ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
4395
- describedBy = computed(() => {
4396
- if (this.error()) {
4397
- return this.errorId;
4398
- }
4399
- if (this.hint()) {
4400
- return this.hintId;
4401
- }
4402
- return null;
4403
- }, /* @ts-ignore */
4404
- ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
4405
- onChange = () => undefined;
4406
- onTouched = () => undefined;
4407
- trackOption(opt) {
4408
- return String(opt.value);
4409
- }
4410
- isSelected(opt) {
4411
- return Object.is(this.value(), opt.value);
4412
- }
4413
- markClass(opt) {
4414
- const base = 'pointer-events-none flex size-4 items-center justify-center rounded-full border-2 bg-transparent transition-colors ' +
4415
- 'peer-focus-visible:outline peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-ink';
4416
- const err = this.error() ? ' !border-danger dark:!border-danger' : '';
4417
- if (this.isSelected(opt)) {
4418
- // Ring uses mode accent; fill stays transparent so the center dot reads clearly.
4419
- return `${base} ${this.modeColor.classes().border}${err}`;
4420
- }
4421
- return `${base} border-neutral-400 dark:border-white${err}`;
4422
- }
4423
- /** Selected inner disc — SFN green / STN orange. */
4424
- dotClass = computed(() => `block size-1.5 rounded-full ${this.modeColor.classes().bg}`, /* @ts-ignore */
4425
- ...(ngDevMode ? [{ debugName: "dotClass" }] : /* istanbul ignore next */ []));
4426
- /** @param value - Selected option value or `null`. */
4427
- writeValue(value) {
4428
- this.value.set(value);
4429
- }
4430
- /** @param fn - Selection change callback. */
4431
- registerOnChange(fn) {
4432
- this.onChange = fn;
4433
- }
4434
- /** @param fn - Blur callback. */
4435
- registerOnTouched(fn) {
4436
- this.onTouched = fn;
4437
- }
4438
- /** @param isDisabled - Blocks interaction when true. */
4439
- setDisabledState(isDisabled) {
4440
- this.cvaDisabled.set(isDisabled);
4441
- }
4442
- select(opt) {
4443
- if (this.disabled() || opt.disabled) {
4444
- return;
4445
- }
4446
- this.value.set(opt.value);
4447
- this.onChange(opt.value);
4448
- }
4449
- onBlur() {
4450
- this.onTouched();
4451
- }
4452
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: RadioComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4453
- 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: [
4454
- {
4455
- provide: NG_VALUE_ACCESSOR,
4456
- useExisting: forwardRef(() => RadioComponent),
4457
- multi: true,
4458
- },
4459
- ], ngImport: i0, template: `
4460
- <fieldset
4461
- class="m-0 p-0 border-0 min-w-0"
4462
- [attr.aria-invalid]="error() ? true : null"
4463
- [attr.aria-describedby]="describedBy()"
4464
- >
4465
- <legend [class]="labelClass">{{ label() }}</legend>
4466
- <div class="flex flex-row flex-wrap gap-x-4 gap-y-2" role="radiogroup">
4467
- @for (opt of options(); track trackOption(opt)) {
4468
- <label
4469
- class="inline-flex items-center gap-2.5 text-body text-ink dark:text-white"
4470
- [class.opacity-50]="disabled() || opt.disabled"
4471
- [class.cursor-pointer]="!(disabled() || opt.disabled)"
4472
- [class.cursor-not-allowed]="disabled() || opt.disabled"
4473
- >
4474
- <span class="relative inline-flex size-4 shrink-0">
4475
- <input
4476
- type="radio"
4477
- class="peer absolute inset-0 z-10 m-0 size-full opacity-0"
4478
- [class.cursor-pointer]="!(disabled() || opt.disabled)"
4479
- [class.cursor-not-allowed]="disabled() || opt.disabled"
4480
- [name]="groupName"
4481
- [checked]="isSelected(opt)"
4482
- [disabled]="disabled() || !!opt.disabled"
4483
- (change)="select(opt)"
4484
- (blur)="onBlur()"
4485
- />
4486
- <span [class]="markClass(opt)" aria-hidden="true">
4487
- @if (isSelected(opt)) {
4488
- <span [class]="dotClass()"></span>
4489
- }
4490
- </span>
4491
- </span>
4492
- <span>{{ opt.label }}</span>
4493
- </label>
4494
- }
4495
- </div>
4496
- @if (error(); as err) {
4497
- <p [id]="errorId" [class]="errorClass" role="alert">{{ err }}</p>
4498
- } @else if (hint(); as h) {
4499
- <p [id]="hintId" [class]="hintClass">{{ h }}</p>
4500
- }
4501
- </fieldset>
4502
- `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
4503
- }
4504
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: RadioComponent, decorators: [{
4505
- type: Component,
4506
- args: [{
4507
- selector: 'africanies-radio',
4508
- standalone: true,
4509
- changeDetection: ChangeDetectionStrategy.OnPush,
4510
- host: {
4511
- class: 'inline-flex max-w-full',
4512
- },
4513
- providers: [
4514
- {
4515
- provide: NG_VALUE_ACCESSOR,
4516
- useExisting: forwardRef(() => RadioComponent),
4517
- multi: true,
4518
- },
4519
- ],
4520
- template: `
4521
- <fieldset
4522
- class="m-0 p-0 border-0 min-w-0"
4523
- [attr.aria-invalid]="error() ? true : null"
4524
- [attr.aria-describedby]="describedBy()"
4525
- >
4526
- <legend [class]="labelClass">{{ label() }}</legend>
4527
- <div class="flex flex-row flex-wrap gap-x-4 gap-y-2" role="radiogroup">
4528
- @for (opt of options(); track trackOption(opt)) {
4529
- <label
4530
- class="inline-flex items-center gap-2.5 text-body text-ink dark:text-white"
4531
- [class.opacity-50]="disabled() || opt.disabled"
4532
- [class.cursor-pointer]="!(disabled() || opt.disabled)"
4533
- [class.cursor-not-allowed]="disabled() || opt.disabled"
4534
- >
4535
- <span class="relative inline-flex size-4 shrink-0">
4536
- <input
4537
- type="radio"
4538
- class="peer absolute inset-0 z-10 m-0 size-full opacity-0"
4539
- [class.cursor-pointer]="!(disabled() || opt.disabled)"
4540
- [class.cursor-not-allowed]="disabled() || opt.disabled"
4541
- [name]="groupName"
4542
- [checked]="isSelected(opt)"
4543
- [disabled]="disabled() || !!opt.disabled"
4544
- (change)="select(opt)"
4545
- (blur)="onBlur()"
4546
- />
4547
- <span [class]="markClass(opt)" aria-hidden="true">
4548
- @if (isSelected(opt)) {
4549
- <span [class]="dotClass()"></span>
4550
- }
4551
- </span>
4552
- </span>
4553
- <span>{{ opt.label }}</span>
4554
- </label>
4555
- }
4556
- </div>
4557
- @if (error(); as err) {
4558
- <p [id]="errorId" [class]="errorClass" role="alert">{{ err }}</p>
4559
- } @else if (hint(); as h) {
4560
- <p [id]="hintId" [class]="hintClass">{{ h }}</p>
4561
- }
4562
- </fieldset>
4563
- `,
4564
- }]
4565
- }], 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 }] }] } });
4566
-
4567
4369
  /**
4568
4370
  * Tailwind panel classes for each {@link ModalSize}.
4569
4371
  *
@@ -4873,16 +4675,16 @@ class ModalService {
4873
4675
  'africanies-overlay-backdrop',
4874
4676
  'bg-ink/45',
4875
4677
  'backdrop-blur-sm',
4876
- 'dark:bg-ink/60',
4678
+ 'dark:bg-ink-950/60',
4877
4679
  ],
4878
4680
  panelClass: [
4879
4681
  'africanies-modal-panel',
4880
- 'bg-white',
4682
+ 'bg-surface',
4881
4683
  'dark:bg-ink-950',
4882
4684
  'text-ink',
4883
4685
  'dark:text-white',
4884
- 'rounded-lg',
4885
- 'shadow-xl',
4686
+ 'rounded-panel',
4687
+ 'shadow-floating',
4886
4688
  'border',
4887
4689
  'border-border',
4888
4690
  'dark:border-white/15',
@@ -5687,7 +5489,7 @@ class SelectComponent {
5687
5489
  @if (multiple() && selectedList().length) {
5688
5490
  @for (chip of selectedList(); track trackOption(chip)) {
5689
5491
  <span
5690
- class="inline-flex items-center gap-1 rounded-md border border-neutral-300 bg-background-welcome dark:border-white/20 dark:bg-ink text-body-sm text-ink dark:text-white px-2 py-0.5"
5492
+ class="inline-flex items-center gap-1 rounded-md border border-neutral-300 bg-background-welcome dark:border-white/20 dark:bg-ink-950 text-body-sm text-ink dark:text-white px-2 py-0.5"
5691
5493
  >
5692
5494
  @if (chip.prefix; as prefixIcon) {
5693
5495
  <africanies-icon [name]="prefixIcon" [size]="14" class="shrink-0" />
@@ -5806,7 +5608,7 @@ class SelectComponent {
5806
5608
  <input
5807
5609
  #searchInput
5808
5610
  type="search"
5809
- 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"
5810
5612
  [value]="searchQuery()"
5811
5613
  placeholder="Search…"
5812
5614
  (input)="onSearchInput($event)"
@@ -6015,7 +5817,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
6015
5817
  @if (multiple() && selectedList().length) {
6016
5818
  @for (chip of selectedList(); track trackOption(chip)) {
6017
5819
  <span
6018
- class="inline-flex items-center gap-1 rounded-md border border-neutral-300 bg-background-welcome dark:border-white/20 dark:bg-ink text-body-sm text-ink dark:text-white px-2 py-0.5"
5820
+ class="inline-flex items-center gap-1 rounded-md border border-neutral-300 bg-background-welcome dark:border-white/20 dark:bg-ink-950 text-body-sm text-ink dark:text-white px-2 py-0.5"
6019
5821
  >
6020
5822
  @if (chip.prefix; as prefixIcon) {
6021
5823
  <africanies-icon [name]="prefixIcon" [size]="14" class="shrink-0" />
@@ -6134,7 +5936,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
6134
5936
  <input
6135
5937
  #searchInput
6136
5938
  type="search"
6137
- 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"
6138
5940
  [value]="searchQuery()"
6139
5941
  placeholder="Search…"
6140
5942
  (input)="onSearchInput($event)"
@@ -6801,13 +6603,6 @@ class FilterDrawerPanel {
6801
6603
  const opt = Array.isArray(selected) ? selected[0] : selected;
6802
6604
  this.draft.update((s) => ({ ...s, order: opt?.value }));
6803
6605
  }
6804
- onRadioChange(key, value) {
6805
- if (value == null) {
6806
- this.clearField(key);
6807
- return;
6808
- }
6809
- this.setFieldValue(key, value);
6810
- }
6811
6606
  onSelectChange(key, selected) {
6812
6607
  const opt = Array.isArray(selected) ? selected[0] : selected;
6813
6608
  if (!opt) {
@@ -6948,11 +6743,18 @@ class FilterDrawerPanel {
6948
6743
  value,
6949
6744
  });
6950
6745
  }
6951
- toRadioOptions(options) {
6952
- return (options ?? []).map((o) => ({
6953
- label: o.label,
6954
- value: o.value,
6955
- }));
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
+ ];
6956
6758
  }
6957
6759
  onReset() {
6958
6760
  const next = resetFilterState(true, this.draft());
@@ -7201,23 +7003,48 @@ class FilterDrawerPanel {
7201
7003
  </div>
7202
7004
  }
7203
7005
  @case ('boolean') {
7204
- <africanies-radio
7205
- label=""
7206
- [options]="toRadioOptions(field.options)"
7207
- [value]="draft().values[field.key] ?? null"
7208
- (valueChange)="onRadioChange(field.key, $event)"
7209
- />
7210
- }
7211
- @case ('select') {
7212
- <!-- Label already shown in the section header — avoid double label. -->
7213
- <africanies-select
7214
- label=""
7215
- [searchable]="true"
7216
- [placeholder]="field.placeholder ?? 'Select…'"
7217
- [options]="selectOptionsFor(field)"
7218
- [selected]="selectedForField(field)"
7219
- [error]="optionErrorFor(field.key)"
7220
- [loading]="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)"
7221
7048
  [retrying]="optionLoadingFor(field.key)"
7222
7049
  [showRetry]="true"
7223
7050
  (retry)="retryFieldOptions(field.key)"
@@ -7265,7 +7092,7 @@ class FilterDrawerPanel {
7265
7092
  type="button"
7266
7093
  variant="ghost"
7267
7094
  size="sm"
7268
- 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"
7269
7096
  aria-label="Dismiss"
7270
7097
  (click)="applyError.set(null)"
7271
7098
  >
@@ -7297,7 +7124,7 @@ class FilterDrawerPanel {
7297
7124
  </div>
7298
7125
  </div>
7299
7126
  </div>
7300
- `, 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 });
7301
7128
  }
7302
7129
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FilterDrawerPanel, decorators: [{
7303
7130
  type: Component,
@@ -7312,7 +7139,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
7312
7139
  AfricaniesIconComponent,
7313
7140
  ButtonComponent,
7314
7141
  DatePickerComponent,
7315
- RadioComponent,
7316
7142
  SelectComponent,
7317
7143
  TextInputComponent,
7318
7144
  ],
@@ -7470,12 +7296,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
7470
7296
  </div>
7471
7297
  }
7472
7298
  @case ('boolean') {
7473
- <africanies-radio
7474
- label=""
7475
- [options]="toRadioOptions(field.options)"
7476
- [value]="draft().values[field.key] ?? null"
7477
- (valueChange)="onRadioChange(field.key, $event)"
7478
- />
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>
7479
7330
  }
7480
7331
  @case ('select') {
7481
7332
  <!-- Label already shown in the section header — avoid double label. -->
@@ -7534,7 +7385,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
7534
7385
  type="button"
7535
7386
  variant="ghost"
7536
7387
  size="sm"
7537
- 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"
7538
7389
  aria-label="Dismiss"
7539
7390
  (click)="applyError.set(null)"
7540
7391
  >
@@ -7610,15 +7461,15 @@ class DrawerService {
7610
7461
  'africanies-overlay-backdrop',
7611
7462
  'bg-ink/45',
7612
7463
  'backdrop-blur-sm',
7613
- 'dark:bg-ink/60',
7464
+ 'dark:bg-ink-950/60',
7614
7465
  ],
7615
7466
  panelClass: [
7616
7467
  'africanies-drawer-panel',
7617
- 'bg-white',
7468
+ 'bg-surface',
7618
7469
  'dark:bg-ink-950',
7619
7470
  'text-ink',
7620
7471
  'dark:text-white',
7621
- 'shadow-xl',
7472
+ 'shadow-floating',
7622
7473
  'border-l',
7623
7474
  'border-border',
7624
7475
  'dark:border-white/15',
@@ -7751,8 +7602,8 @@ class ConfirmDialogComponent {
7751
7602
  iconWellClass = computed(() => {
7752
7603
  const well = 'inline-flex size-14 shrink-0 items-center justify-center rounded-2xl';
7753
7604
  return this.options.danger
7754
- ? `${well} bg-danger-subtle text-danger dark:bg-danger/20 dark:text-danger`
7755
- : `${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`;
7756
7607
  }, /* @ts-ignore */
7757
7608
  ...(ngDevMode ? [{ debugName: "iconWellClass" }] : /* istanbul ignore next */ []));
7758
7609
  constructor() {
@@ -8138,7 +7989,7 @@ class OverlayFrameComponent {
8138
7989
  <ng-content select="[africaniesOverlayHeader]" />
8139
7990
  </div>
8140
7991
  <div
8141
- 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"
8142
7993
  >
8143
7994
  <ng-content />
8144
7995
  </div>
@@ -8161,7 +8012,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
8161
8012
  <ng-content select="[africaniesOverlayHeader]" />
8162
8013
  </div>
8163
8014
  <div
8164
- 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"
8165
8016
  >
8166
8017
  <ng-content />
8167
8018
  </div>
@@ -9053,7 +8904,7 @@ class CheckboxComponent {
9053
8904
  ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
9054
8905
  markClass = computed(() => {
9055
8906
  const base = 'pointer-events-none flex size-4 items-center justify-center rounded border text-white transition-colors ' +
9056
- '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;
9057
8908
  const err = this.error() ? ' !border-danger dark:!border-danger' : '';
9058
8909
  if (this.value()) {
9059
8910
  const c = this.modeColor.classes();
@@ -9722,13 +9573,13 @@ class FilePreviewDialogComponent {
9722
9573
  <img
9723
9574
  [src]="src()"
9724
9575
  [alt]="data.file.name"
9725
- class="max-h-[min(70vh,40rem)] w-full rounded-md object-contain bg-background-welcome ring-1 ring-border dark:bg-ink dark:ring-white/15"
9576
+ class="max-h-[min(70vh,40rem)] w-full rounded-md object-contain bg-background-welcome ring-1 ring-border dark:bg-ink-950 dark:ring-white/15"
9726
9577
  />
9727
9578
  } @else {
9728
9579
  <div
9729
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"
9730
9581
  >
9731
- <africanies-icon name="eye-slash" [size]="32" class="text-neutral-500" />
9582
+ <africanies-icon name="eye-slash" [size]="32" class="text-neutral-600" />
9732
9583
  <p class="m-0 text-body font-medium text-ink dark:text-white">
9733
9584
  Preview not available
9734
9585
  </p>
@@ -9792,13 +9643,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
9792
9643
  <img
9793
9644
  [src]="src()"
9794
9645
  [alt]="data.file.name"
9795
- class="max-h-[min(70vh,40rem)] w-full rounded-md object-contain bg-background-welcome ring-1 ring-border dark:bg-ink dark:ring-white/15"
9646
+ class="max-h-[min(70vh,40rem)] w-full rounded-md object-contain bg-background-welcome ring-1 ring-border dark:bg-ink-950 dark:ring-white/15"
9796
9647
  />
9797
9648
  } @else {
9798
9649
  <div
9799
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"
9800
9651
  >
9801
- <africanies-icon name="eye-slash" [size]="32" class="text-neutral-500" />
9652
+ <africanies-icon name="eye-slash" [size]="32" class="text-neutral-600" />
9802
9653
  <p class="m-0 text-body font-medium text-ink dark:text-white">
9803
9654
  Preview not available
9804
9655
  </p>
@@ -10275,7 +10126,7 @@ class FileUploadComponent {
10275
10126
  <button
10276
10127
  africanies-button
10277
10128
  type="button"
10278
- variant="secondary"
10129
+ variant="flat"
10279
10130
  size="sm"
10280
10131
  [disabled]="disabled()"
10281
10132
  (click)="openFilePicker()"
@@ -10288,7 +10139,7 @@ class FileUploadComponent {
10288
10139
  <button
10289
10140
  africanies-button
10290
10141
  type="button"
10291
- variant="secondary"
10142
+ variant="flat"
10292
10143
  size="sm"
10293
10144
  [disabled]="disabled()"
10294
10145
  (click)="openCamera()"
@@ -10343,7 +10194,7 @@ class FileUploadComponent {
10343
10194
  <button
10344
10195
  africanies-button
10345
10196
  type="button"
10346
- variant="secondary"
10197
+ variant="flat"
10347
10198
  size="sm"
10348
10199
  [disabled]="disabled()"
10349
10200
  (click)="openFilePicker()"
@@ -10412,7 +10263,7 @@ class FileUploadComponent {
10412
10263
  <button
10413
10264
  africanies-button
10414
10265
  type="button"
10415
- variant="secondary"
10266
+ variant="flat"
10416
10267
  size="sm"
10417
10268
  [disabled]="disabled()"
10418
10269
  (click)="openFilePicker(); $event.stopPropagation()"
@@ -10491,7 +10342,7 @@ class FileUploadComponent {
10491
10342
  <span> · </span>
10492
10343
  <button
10493
10344
  type="button"
10494
- 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"
10495
10346
  (click)="openPreview(item); $event.stopPropagation()"
10496
10347
  >
10497
10348
  View
@@ -10575,7 +10426,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
10575
10426
  <button
10576
10427
  africanies-button
10577
10428
  type="button"
10578
- variant="secondary"
10429
+ variant="flat"
10579
10430
  size="sm"
10580
10431
  [disabled]="disabled()"
10581
10432
  (click)="openFilePicker()"
@@ -10588,7 +10439,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
10588
10439
  <button
10589
10440
  africanies-button
10590
10441
  type="button"
10591
- variant="secondary"
10442
+ variant="flat"
10592
10443
  size="sm"
10593
10444
  [disabled]="disabled()"
10594
10445
  (click)="openCamera()"
@@ -10643,7 +10494,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
10643
10494
  <button
10644
10495
  africanies-button
10645
10496
  type="button"
10646
- variant="secondary"
10497
+ variant="flat"
10647
10498
  size="sm"
10648
10499
  [disabled]="disabled()"
10649
10500
  (click)="openFilePicker()"
@@ -10712,7 +10563,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
10712
10563
  <button
10713
10564
  africanies-button
10714
10565
  type="button"
10715
- variant="secondary"
10566
+ variant="flat"
10716
10567
  size="sm"
10717
10568
  [disabled]="disabled()"
10718
10569
  (click)="openFilePicker(); $event.stopPropagation()"
@@ -10791,7 +10642,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
10791
10642
  <span> · </span>
10792
10643
  <button
10793
10644
  type="button"
10794
- 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"
10795
10646
  (click)="openPreview(item); $event.stopPropagation()"
10796
10647
  >
10797
10648
  View
@@ -11205,7 +11056,7 @@ class OtpInputComponent {
11205
11056
  cellClass = computed(() => {
11206
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 ' +
11207
11058
  'dark:border-white/25 dark:bg-ink-950 dark:text-white ' +
11208
- 'focus:outline focus:outline-2 focus:outline-offset-2 focus:outline-ink disabled:cursor-not-allowed';
11059
+ `${FORM_FOCUS_CLASS} disabled:cursor-not-allowed`;
11209
11060
  if (this.masked()) {
11210
11061
  classes += ' africanies-otp-cell-masked text-body-lg';
11211
11062
  }
@@ -11456,48 +11307,314 @@ class OtpInputComponent {
11456
11307
  el?.select();
11457
11308
  });
11458
11309
  }
11459
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: OtpInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11460
- 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: [
11461
11578
  {
11462
11579
  provide: NG_VALUE_ACCESSOR,
11463
- useExisting: forwardRef(() => OtpInputComponent),
11580
+ useExisting: forwardRef(() => RadioComponent),
11464
11581
  multi: true,
11465
11582
  },
11466
- ], viewQueries: [{ propertyName: "cellInputs", predicate: ["cellInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
11467
- <div
11468
- role="group"
11469
- [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"
11470
11587
  [attr.aria-describedby]="describedBy()"
11471
- [attr.aria-disabled]="disabled() ? true : null"
11472
11588
  >
11473
- @if (label()) {
11474
- <p [id]="labelId" [class]="labelClass">{{ label() }}</p>
11475
- }
11476
- <div
11477
- class="flex flex-wrap gap-2"
11478
- [class.opacity-50]="disabled()"
11479
- [class.cursor-not-allowed]="disabled()"
11480
- >
11481
- @for (digit of cells(); track $index; let i = $index) {
11482
- <input
11483
- #cellInput
11484
- [type]="cellType()"
11485
- inputmode="numeric"
11486
- pattern="[0-9]*"
11487
- maxlength="1"
11488
- [class]="cellClass()"
11489
- [id]="i === 0 ? controlId : null"
11490
- [value]="digit"
11491
- [disabled]="disabled()"
11492
- [attr.autocomplete]="i === 0 ? 'one-time-code' : 'off'"
11493
- [attr.aria-label]="'Digit ' + (i + 1) + ' of ' + length()"
11494
- [attr.aria-invalid]="error() ? true : null"
11495
- (input)="onCellInput(i, $event)"
11496
- (keydown)="onCellKeydown(i, $event)"
11497
- (paste)="onPaste(i, $event)"
11498
- (focus)="onCellFocus(i)"
11499
- (blur)="onBlur()"
11500
- />
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>
11501
11618
  }
11502
11619
  </div>
11503
11620
  @if (error(); as err) {
@@ -11505,79 +11622,60 @@ class OtpInputComponent {
11505
11622
  } @else if (hint(); as h) {
11506
11623
  <p [id]="hintId" [class]="hintClass">{{ h }}</p>
11507
11624
  }
11508
-
11509
- @if (showResend()) {
11510
- <div
11511
- 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"
11512
- >
11513
- <span>{{ resendPrompt() }}</span>
11514
- @if (cooldownRemaining() > 0) {
11515
- <span class="tabular-nums" aria-live="polite">
11516
- Resend in {{ cooldownRemaining() }}s
11517
- </span>
11518
- } @else {
11519
- <button
11520
- africanies-button
11521
- type="button"
11522
- variant="ghost"
11523
- size="sm"
11524
- class="!min-h-0 !px-1.5 !py-0.5 !text-body-sm"
11525
- [disabled]="disabled()"
11526
- (click)="onResendClick()"
11527
- >
11528
- {{ resendLabel() }}
11529
- </button>
11530
- }
11531
- </div>
11532
- }
11533
- </div>
11534
- `, 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 });
11535
11627
  }
11536
- 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: [{
11537
11629
  type: Component,
11538
- 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: [
11539
11638
  {
11540
11639
  provide: NG_VALUE_ACCESSOR,
11541
- useExisting: forwardRef(() => OtpInputComponent),
11640
+ useExisting: forwardRef(() => RadioComponent),
11542
11641
  multi: true,
11543
11642
  },
11544
- ], host: {
11545
- class: 'block',
11546
- }, template: `
11547
- <div
11548
- role="group"
11549
- [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"
11550
11648
  [attr.aria-describedby]="describedBy()"
11551
- [attr.aria-disabled]="disabled() ? true : null"
11552
11649
  >
11553
- @if (label()) {
11554
- <p [id]="labelId" [class]="labelClass">{{ label() }}</p>
11555
- }
11556
- <div
11557
- class="flex flex-wrap gap-2"
11558
- [class.opacity-50]="disabled()"
11559
- [class.cursor-not-allowed]="disabled()"
11560
- >
11561
- @for (digit of cells(); track $index; let i = $index) {
11562
- <input
11563
- #cellInput
11564
- [type]="cellType()"
11565
- inputmode="numeric"
11566
- pattern="[0-9]*"
11567
- maxlength="1"
11568
- [class]="cellClass()"
11569
- [id]="i === 0 ? controlId : null"
11570
- [value]="digit"
11571
- [disabled]="disabled()"
11572
- [attr.autocomplete]="i === 0 ? 'one-time-code' : 'off'"
11573
- [attr.aria-label]="'Digit ' + (i + 1) + ' of ' + length()"
11574
- [attr.aria-invalid]="error() ? true : null"
11575
- (input)="onCellInput(i, $event)"
11576
- (keydown)="onCellKeydown(i, $event)"
11577
- (paste)="onPaste(i, $event)"
11578
- (focus)="onCellFocus(i)"
11579
- (blur)="onBlur()"
11580
- />
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>
11581
11679
  }
11582
11680
  </div>
11583
11681
  @if (error(); as err) {
@@ -11585,34 +11683,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
11585
11683
  } @else if (hint(); as h) {
11586
11684
  <p [id]="hintId" [class]="hintClass">{{ h }}</p>
11587
11685
  }
11588
-
11589
- @if (showResend()) {
11590
- <div
11591
- 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"
11592
- >
11593
- <span>{{ resendPrompt() }}</span>
11594
- @if (cooldownRemaining() > 0) {
11595
- <span class="tabular-nums" aria-live="polite">
11596
- Resend in {{ cooldownRemaining() }}s
11597
- </span>
11598
- } @else {
11599
- <button
11600
- africanies-button
11601
- type="button"
11602
- variant="ghost"
11603
- size="sm"
11604
- class="!min-h-0 !px-1.5 !py-0.5 !text-body-sm"
11605
- [disabled]="disabled()"
11606
- (click)="onResendClick()"
11607
- >
11608
- {{ resendLabel() }}
11609
- </button>
11610
- }
11611
- </div>
11612
- }
11613
- </div>
11614
- `, styles: [".africanies-otp-cell-masked{-webkit-text-security:disc;text-security:disc}\n"] }]
11615
- }], 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 }] }] } });
11616
11690
 
11617
11691
  /**
11618
11692
  * Radio form control exports.
@@ -12600,7 +12674,7 @@ class ToggleComponent {
12600
12674
  [id]="controlId"
12601
12675
  type="button"
12602
12676
  role="switch"
12603
- 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"
12604
12678
  [ngClass]="trackClass()"
12605
12679
  [attr.aria-checked]="on()"
12606
12680
  [attr.aria-busy]="loading() ? true : null"
@@ -12662,7 +12736,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
12662
12736
  [id]="controlId"
12663
12737
  type="button"
12664
12738
  role="switch"
12665
- 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"
12666
12740
  [ngClass]="trackClass()"
12667
12741
  [attr.aria-checked]="on()"
12668
12742
  [attr.aria-busy]="loading() ? true : null"
@@ -13498,16 +13572,16 @@ class ShippingModeSwitchComponent {
13498
13572
  const selected = this.mode() === option;
13499
13573
  const compact = this.collapsed();
13500
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 ' +
13501
- '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 ' +
13502
13576
  (compact ? 'min-h-9 px-1 py-1.5' : 'min-h-[4.5rem] px-1.5 py-2');
13503
- 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';
13504
13578
  if (option === 'stn') {
13505
13579
  return selected
13506
- ? `${base} border-import bg-import text-white`
13580
+ ? `${base} border-import-strong bg-import-strong text-white dark:border-import dark:bg-import`
13507
13581
  : `${base} ${idle}`;
13508
13582
  }
13509
13583
  return selected
13510
- ? `${base} border-export bg-export text-white`
13584
+ ? `${base} border-export-strong bg-export-strong text-white dark:border-export dark:bg-export`
13511
13585
  : `${base} ${idle}`;
13512
13586
  }
13513
13587
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ShippingModeSwitchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
@@ -13786,14 +13860,16 @@ const DEFAULT_LINK_ACTIVE$1 = {
13786
13860
  /**
13787
13861
  * App shell side navigation with icons, nested items, and a collapsible rail.
13788
13862
  *
13789
- * **Design - ink spine:** a continuous mode-accent edge with soft active
13790
- * highlights. Collapsed mode keeps icons; hover opens a floating label blade
13791
- * (name + children) instead of a plain tooltip.
13863
+ * **Design - ink spine:** a continuous mode-accent edge; active leaves use
13864
+ * primary fill + white text (portal parity); expanded parents of an active
13865
+ * child use primary text on white. Collapsed mode keeps icons; hover opens a
13866
+ * floating label blade (name + children) instead of a plain tooltip.
13792
13867
  *
13793
13868
  * ## Parent items with children
13794
13869
  * **Expanded rail:** branches start open by default (`expandBranchesByDefault`).
13795
13870
  * Clicking a parent only opens/closes the branch (no route change). When any
13796
- * 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.
13797
13873
  * **Collapsed rail:** clicking a parent expands the rail, opens the
13798
13874
  * branch, and activates the first enabled child (router or `activeId`).
13799
13875
  *
@@ -13906,6 +13982,10 @@ class SideNavComponent {
13906
13982
  collapseAllBranches() {
13907
13983
  this.openBranches.set(new Set());
13908
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
+ }
13909
13989
  onItemEnter(id) {
13910
13990
  if (this.collapsed()) {
13911
13991
  this.hoverId.set(id);
@@ -13938,28 +14018,31 @@ class SideNavComponent {
13938
14018
  }
13939
14019
  rowClass(item) {
13940
14020
  const base = 'group relative flex w-full items-center gap-2 rounded-lg text-body-sm no-underline transition-colors ' +
13941
- '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 ' +
13942
14022
  (this.collapsed() ? 'justify-center px-1.5 py-2' : 'px-3 py-2') +
13943
14023
  (item.disabled ? ' cursor-not-allowed opacity-50' : ' cursor-pointer');
13944
14024
  const leaf = this.isLeafActive(item);
13945
14025
  const ancestor = !leaf && this.isItemActive(item);
14026
+ const colors = this.modeColor.classes();
14027
+ // Portal: active leaf / collapsed rail = primary fill + white text.
13946
14028
  if (leaf) {
13947
- return `${base} ${this.modeColor.classes().soft} ${this.modeColor.classes().text} font-semibold`;
14029
+ return `${base} ${colors.activeFill} font-semibold`;
13948
14030
  }
14031
+ // Portal: expanded parent of active child = primary text on white.
13949
14032
  if (ancestor) {
13950
- return `${base} ${this.modeColor.classes().soft} ${this.modeColor.classes().text} font-semibold`;
14033
+ return `${base} bg-transparent ${colors.text} font-semibold`;
13951
14034
  }
13952
- 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 ` +
13953
14036
  'dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white');
13954
14037
  }
13955
14038
  childRowClass(item) {
13956
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 ' +
13957
- '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 ' +
13958
14041
  (item.disabled ? 'cursor-not-allowed opacity-50 ' : 'cursor-pointer ');
13959
14042
  if (this.isLeafActive(item)) {
13960
- return `${base} ${this.modeColor.classes().soft} ${this.modeColor.classes().text} font-semibold`;
14043
+ return `${base} ${this.modeColor.classes().activeFill} font-semibold`;
13961
14044
  }
13962
- 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 ` +
13963
14046
  'dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white');
13964
14047
  }
13965
14048
  /**
@@ -14102,13 +14185,13 @@ class SideNavComponent {
14102
14185
  return null;
14103
14186
  }
14104
14187
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SideNavComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14105
- 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\"\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 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 });
14106
14189
  }
14107
14190
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SideNavComponent, decorators: [{
14108
14191
  type: Component,
14109
14192
  args: [{ selector: 'africanies-side-nav', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AfricaniesIconComponent, RouterLink, NgTemplateOutlet], host: {
14110
- class: 'relative z-20 block h-full',
14111
- }, 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\"\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 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"] }]
14112
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 }] }] } });
14113
14196
 
14114
14197
  /**
@@ -14637,7 +14720,7 @@ class AppShellContentHeaderComponent {
14637
14720
  <div class="flex min-w-0 items-center gap-2 sm:gap-3">
14638
14721
  @if (showBackButton()) {
14639
14722
  <a
14640
- 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"
14641
14724
  [attr.aria-label]="backLabel()"
14642
14725
  [routerLink]="backTarget()!.routerLink"
14643
14726
  [queryParams]="backTarget()!.queryParams"
@@ -14683,7 +14766,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
14683
14766
  <div class="flex min-w-0 items-center gap-2 sm:gap-3">
14684
14767
  @if (showBackButton()) {
14685
14768
  <a
14686
- 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"
14687
14770
  [attr.aria-label]="backLabel()"
14688
14771
  [routerLink]="backTarget()!.routerLink"
14689
14772
  [queryParams]="backTarget()!.queryParams"
@@ -14856,7 +14939,7 @@ class AvatarMenuComponent {
14856
14939
  <button
14857
14940
  type="button"
14858
14941
  africaniesActionMenuTrigger
14859
- 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"
14860
14943
  [attr.aria-label]="ariaLabel()"
14861
14944
  >
14862
14945
  <africanies-avatar [name]="name()" [src]="src()" [size]="size()" />
@@ -14864,7 +14947,7 @@ class AvatarMenuComponent {
14864
14947
  <africanies-icon
14865
14948
  name="chevron-down"
14866
14949
  [size]="14"
14867
- class="shrink-0 text-neutral-500 dark:text-neutral-400"
14950
+ class="shrink-0 text-neutral-600 dark:text-neutral-400"
14868
14951
  aria-hidden="true"
14869
14952
  />
14870
14953
  }
@@ -14893,7 +14976,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
14893
14976
  <button
14894
14977
  type="button"
14895
14978
  africaniesActionMenuTrigger
14896
- 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"
14897
14980
  [attr.aria-label]="ariaLabel()"
14898
14981
  >
14899
14982
  <africanies-avatar [name]="name()" [src]="src()" [size]="size()" />
@@ -14901,7 +14984,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
14901
14984
  <africanies-icon
14902
14985
  name="chevron-down"
14903
14986
  [size]="14"
14904
- class="shrink-0 text-neutral-500 dark:text-neutral-400"
14987
+ class="shrink-0 text-neutral-600 dark:text-neutral-400"
14905
14988
  aria-hidden="true"
14906
14989
  />
14907
14990
  }
@@ -14934,14 +15017,15 @@ class NotificationDrawerPanel {
14934
15017
  viewLinkClass = computed(() => {
14935
15018
  const base = 'inline-flex cursor-pointer items-center gap-1 self-start text-caption font-semibold underline underline-offset-[3px] transition-colors';
14936
15019
  if (this.shipping.mode() === 'stn') {
14937
- 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`;
14938
15021
  }
14939
- 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`;
14940
15023
  }, /* @ts-ignore */
14941
15024
  ...(ngDevMode ? [{ debugName: "viewLinkClass" }] : /* istanbul ignore next */ []));
14942
15025
  title = computed(() => this.data.title ?? 'Notifications', /* @ts-ignore */
14943
15026
  ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
14944
- items = signal(this.data.onLoadPage ? [] : (this.data.notifications ?? []), /* @ts-ignore */
15027
+ seededItems = this.data.notifications ?? [];
15028
+ items = signal(this.seededItems, /* @ts-ignore */
14945
15029
  ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
14946
15030
  markingId = signal(null, /* @ts-ignore */
14947
15031
  ...(ngDevMode ? [{ debugName: "markingId" }] : /* istanbul ignore next */ []));
@@ -14949,7 +15033,7 @@ class NotificationDrawerPanel {
14949
15033
  ...(ngDevMode ? [{ debugName: "markingAll" }] : /* istanbul ignore next */ []));
14950
15034
  confirmingMarkAll = signal(false, /* @ts-ignore */
14951
15035
  ...(ngDevMode ? [{ debugName: "confirmingMarkAll" }] : /* istanbul ignore next */ []));
14952
- loadingInitial = signal(!!this.data.onLoadPage, /* @ts-ignore */
15036
+ loadingInitial = signal(!!this.data.onLoadPage && this.seededItems.length === 0, /* @ts-ignore */
14953
15037
  ...(ngDevMode ? [{ debugName: "loadingInitial" }] : /* istanbul ignore next */ []));
14954
15038
  loadingMore = signal(false, /* @ts-ignore */
14955
15039
  ...(ngDevMode ? [{ debugName: "loadingMore" }] : /* istanbul ignore next */ []));
@@ -14983,7 +15067,13 @@ class NotificationDrawerPanel {
14983
15067
  markAllConfirmTimer = null;
14984
15068
  constructor() {
14985
15069
  if (this.data.onLoadPage) {
14986
- this.fetchPage(1, 'replace');
15070
+ if (this.seededItems.length > 0) {
15071
+ this.currentPage.set(1);
15072
+ this.fetchPage(1, 'refresh');
15073
+ }
15074
+ else {
15075
+ this.fetchPage(1, 'replace');
15076
+ }
14987
15077
  }
14988
15078
  effect(() => {
14989
15079
  if (this.loadingInitial() ||
@@ -15177,11 +15267,14 @@ class NotificationDrawerPanel {
15177
15267
  this.loadingInitial.set(true);
15178
15268
  this.loadError.set(null);
15179
15269
  }
15270
+ else if (mode === 'refresh') {
15271
+ this.loadError.set(null);
15272
+ }
15180
15273
  else {
15181
15274
  this.loadingMore.set(true);
15182
15275
  }
15183
15276
  this.runPageLoad(() => loadPage(page), (result) => {
15184
- if (mode === 'replace') {
15277
+ if (mode === 'replace' || mode === 'refresh') {
15185
15278
  this.items.set(result.items ?? []);
15186
15279
  }
15187
15280
  else {
@@ -15196,6 +15289,9 @@ class NotificationDrawerPanel {
15196
15289
  this.loadError.set('Could not load notifications. Try again.');
15197
15290
  this.loadingInitial.set(false);
15198
15291
  }
15292
+ else if (mode === 'refresh' && this.items().length === 0) {
15293
+ this.loadError.set('Could not load notifications. Try again.');
15294
+ }
15199
15295
  this.loadingMore.set(false);
15200
15296
  });
15201
15297
  }
@@ -15269,7 +15365,7 @@ class NotificationDrawerPanel {
15269
15365
  {{ title() }}
15270
15366
  </h2>
15271
15367
  @if (hasUnread()) {
15272
- <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">
15273
15369
  {{ unreadCount() }} unread
15274
15370
  </p>
15275
15371
  }
@@ -15315,7 +15411,7 @@ class NotificationDrawerPanel {
15315
15411
  <africanies-icon
15316
15412
  name="spinner"
15317
15413
  [size]="24"
15318
- class="animate-spin text-neutral-500 dark:text-neutral-400"
15414
+ class="animate-spin text-neutral-600 dark:text-neutral-400"
15319
15415
  />
15320
15416
  <p class="m-0 text-body-sm text-neutral-600 dark:text-neutral-400">
15321
15417
  Loading notifications…
@@ -15330,7 +15426,7 @@ class NotificationDrawerPanel {
15330
15426
  <button
15331
15427
  africanies-button
15332
15428
  type="button"
15333
- variant="secondary"
15429
+ variant="flat"
15334
15430
  size="sm"
15335
15431
  (click)="retryInitialLoad()"
15336
15432
  >
@@ -15382,7 +15478,7 @@ class NotificationDrawerPanel {
15382
15478
  </h3>
15383
15479
  @if (item.timestamp) {
15384
15480
  <time
15385
- 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"
15386
15482
  [dateTime]="item.timestamp"
15387
15483
  >
15388
15484
  {{ formatTimestamp(item.timestamp) }}
@@ -15461,7 +15557,7 @@ class NotificationDrawerPanel {
15461
15557
  >
15462
15558
  @if (loadingMore()) {
15463
15559
  <span
15464
- 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"
15465
15561
  aria-live="polite"
15466
15562
  >
15467
15563
  <africanies-icon name="spinner" [size]="14" class="animate-spin" />
@@ -15500,7 +15596,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
15500
15596
  {{ title() }}
15501
15597
  </h2>
15502
15598
  @if (hasUnread()) {
15503
- <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">
15504
15600
  {{ unreadCount() }} unread
15505
15601
  </p>
15506
15602
  }
@@ -15546,7 +15642,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
15546
15642
  <africanies-icon
15547
15643
  name="spinner"
15548
15644
  [size]="24"
15549
- class="animate-spin text-neutral-500 dark:text-neutral-400"
15645
+ class="animate-spin text-neutral-600 dark:text-neutral-400"
15550
15646
  />
15551
15647
  <p class="m-0 text-body-sm text-neutral-600 dark:text-neutral-400">
15552
15648
  Loading notifications…
@@ -15561,7 +15657,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
15561
15657
  <button
15562
15658
  africanies-button
15563
15659
  type="button"
15564
- variant="secondary"
15660
+ variant="flat"
15565
15661
  size="sm"
15566
15662
  (click)="retryInitialLoad()"
15567
15663
  >
@@ -15613,7 +15709,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
15613
15709
  </h3>
15614
15710
  @if (item.timestamp) {
15615
15711
  <time
15616
- 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"
15617
15713
  [dateTime]="item.timestamp"
15618
15714
  >
15619
15715
  {{ formatTimestamp(item.timestamp) }}
@@ -15692,7 +15788,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
15692
15788
  >
15693
15789
  @if (loadingMore()) {
15694
15790
  <span
15695
- 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"
15696
15792
  aria-live="polite"
15697
15793
  >
15698
15794
  <africanies-icon name="spinner" [size]="14" class="animate-spin" />
@@ -16429,7 +16525,7 @@ class AppShellHeaderComponent {
16429
16525
  this.notificationsDrawer
16430
16526
  .open({
16431
16527
  title: this.notificationsTitle(),
16432
- notifications: onLoadPage ? undefined : this.notifications(),
16528
+ notifications: this.notifications(),
16433
16529
  onLoadPage,
16434
16530
  onMarkRead,
16435
16531
  onMarkAllRead,
@@ -16449,7 +16545,7 @@ class AppShellHeaderComponent {
16449
16545
  @if (greeting(); as hello) {
16450
16546
  <div class="min-w-0 overflow-hidden">
16451
16547
  <p
16452
- class="m-0 truncate text-caption leading-none text-neutral-500 dark:text-neutral-400"
16548
+ class="m-0 truncate text-caption leading-none text-neutral-600 dark:text-neutral-400"
16453
16549
  >
16454
16550
  {{ hello.kicker }}
16455
16551
  </p>
@@ -16475,7 +16571,7 @@ class AppShellHeaderComponent {
16475
16571
  <africanies-icon
16476
16572
  [name]="weatherIcon()"
16477
16573
  [size]="18"
16478
- class="text-neutral-500 dark:text-neutral-400"
16574
+ class="text-neutral-600 dark:text-neutral-400"
16479
16575
  />
16480
16576
  @if (weatherTemp(); as temp) {
16481
16577
  <span
@@ -16485,7 +16581,7 @@ class AppShellHeaderComponent {
16485
16581
  </span>
16486
16582
  }
16487
16583
  <span
16488
- class="hidden text-caption text-neutral-500 md:inline dark:text-neutral-400"
16584
+ class="hidden text-caption text-neutral-600 md:inline dark:text-neutral-400"
16489
16585
  >
16490
16586
  {{ weatherPlace() }}
16491
16587
  </span>
@@ -16495,7 +16591,7 @@ class AppShellHeaderComponent {
16495
16591
  @if (showClock()) {
16496
16592
  <div class="flex items-baseline gap-2" aria-live="polite">
16497
16593
  <time
16498
- class="hidden text-caption text-neutral-500 md:inline dark:text-neutral-400"
16594
+ class="hidden text-caption text-neutral-600 md:inline dark:text-neutral-400"
16499
16595
  [dateTime]="nowIso()"
16500
16596
  >
16501
16597
  {{ now() | date: clockDateFormat() }}
@@ -16525,7 +16621,7 @@ class AppShellHeaderComponent {
16525
16621
  @if (showNotifications()) {
16526
16622
  <button
16527
16623
  type="button"
16528
- class="relative inline-flex size-9 cursor-pointer items-center justify-center rounded-lg text-neutral-600 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
16624
+ class="relative inline-flex size-9 cursor-pointer items-center justify-center rounded-lg text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
16529
16625
  [attr.aria-label]="notificationAriaLabel()"
16530
16626
  (click)="openNotifications()"
16531
16627
  >
@@ -16576,7 +16672,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16576
16672
  @if (greeting(); as hello) {
16577
16673
  <div class="min-w-0 overflow-hidden">
16578
16674
  <p
16579
- class="m-0 truncate text-caption leading-none text-neutral-500 dark:text-neutral-400"
16675
+ class="m-0 truncate text-caption leading-none text-neutral-600 dark:text-neutral-400"
16580
16676
  >
16581
16677
  {{ hello.kicker }}
16582
16678
  </p>
@@ -16602,7 +16698,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16602
16698
  <africanies-icon
16603
16699
  [name]="weatherIcon()"
16604
16700
  [size]="18"
16605
- class="text-neutral-500 dark:text-neutral-400"
16701
+ class="text-neutral-600 dark:text-neutral-400"
16606
16702
  />
16607
16703
  @if (weatherTemp(); as temp) {
16608
16704
  <span
@@ -16612,7 +16708,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16612
16708
  </span>
16613
16709
  }
16614
16710
  <span
16615
- class="hidden text-caption text-neutral-500 md:inline dark:text-neutral-400"
16711
+ class="hidden text-caption text-neutral-600 md:inline dark:text-neutral-400"
16616
16712
  >
16617
16713
  {{ weatherPlace() }}
16618
16714
  </span>
@@ -16622,7 +16718,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16622
16718
  @if (showClock()) {
16623
16719
  <div class="flex items-baseline gap-2" aria-live="polite">
16624
16720
  <time
16625
- class="hidden text-caption text-neutral-500 md:inline dark:text-neutral-400"
16721
+ class="hidden text-caption text-neutral-600 md:inline dark:text-neutral-400"
16626
16722
  [dateTime]="nowIso()"
16627
16723
  >
16628
16724
  {{ now() | date: clockDateFormat() }}
@@ -16652,7 +16748,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16652
16748
  @if (showNotifications()) {
16653
16749
  <button
16654
16750
  type="button"
16655
- class="relative inline-flex size-9 cursor-pointer items-center justify-center rounded-lg text-neutral-600 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
16751
+ class="relative inline-flex size-9 cursor-pointer items-center justify-center rounded-lg text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
16656
16752
  [attr.aria-label]="notificationAriaLabel()"
16657
16753
  (click)="openNotifications()"
16658
16754
  >
@@ -16826,8 +16922,8 @@ class AppShellComponent {
16826
16922
  hostClass = computed(() => this.layoutPreview() ? 'block h-full' : 'block min-h-full', /* @ts-ignore */
16827
16923
  ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
16828
16924
  shellRootClass = computed(() => this.layoutPreview()
16829
- ? 'relative flex h-full min-h-0'
16830
- : 'relative flex min-h-full', /* @ts-ignore */
16925
+ ? 'relative flex h-full min-h-0 bg-background-chrome dark:bg-ink-950'
16926
+ : 'relative flex min-h-full bg-background-chrome dark:bg-ink-950', /* @ts-ignore */
16831
16927
  ...(ngDevMode ? [{ debugName: "shellRootClass" }] : /* istanbul ignore next */ []));
16832
16928
  headerDensity = computed(() => {
16833
16929
  const preview = this.layoutPreview();
@@ -16854,14 +16950,14 @@ class AppShellComponent {
16854
16950
  // Preview embeds: absolute/h-full so the drawer stays inside the frame.
16855
16951
  // Production: h-dvh so the rail fills the viewport (h-full only matches parent).
16856
16952
  if (preview === 'desktop') {
16857
- return 'z-50 shrink-0 self-start sticky top-0 block h-full';
16953
+ return 'z-50 shrink-0 self-start sticky top-0 block h-full min-h-0';
16858
16954
  }
16859
16955
  if (preview === 'mobile' || preview === 'tablet') {
16860
16956
  return open
16861
- ? 'z-50 shrink-0 self-start absolute inset-y-0 left-0 h-full shadow-xl'
16957
+ ? 'z-50 shrink-0 self-start absolute inset-y-0 left-0 h-full min-h-0 shadow-xl'
16862
16958
  : 'hidden';
16863
16959
  }
16864
- const base = 'z-50 shrink-0 self-start h-dvh';
16960
+ const base = 'z-50 shrink-0 self-start h-dvh min-h-0';
16865
16961
  if (open) {
16866
16962
  return `${base} fixed inset-y-0 left-0 shadow-xl lg:sticky lg:top-0 lg:block`;
16867
16963
  }
@@ -16897,13 +16993,13 @@ class AppShellComponent {
16897
16993
  this.mobileNavOpen.set(false);
16898
16994
  }
16899
16995
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AppShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
16900
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AppShellComponent, isStandalone: true, selector: "africanies-app-shell", inputs: { contentWidth: { classPropertyName: "contentWidth", publicName: "contentWidth", isSignal: true, isRequired: false, transformFunction: null }, showAccent: { classPropertyName: "showAccent", publicName: "showAccent", isSignal: true, isRequired: false, transformFunction: null }, showHeaderChrome: { classPropertyName: "showHeaderChrome", publicName: "showHeaderChrome", isSignal: true, isRequired: false, transformFunction: null }, layoutPreview: { classPropertyName: "layoutPreview", publicName: "layoutPreview", isSignal: true, isRequired: false, transformFunction: null }, headerTitle: { classPropertyName: "headerTitle", publicName: "headerTitle", isSignal: true, isRequired: false, transformFunction: null }, headerSubtitle: { classPropertyName: "headerSubtitle", publicName: "headerSubtitle", isSignal: true, isRequired: false, transformFunction: null }, breadcrumbs: { classPropertyName: "breadcrumbs", publicName: "breadcrumbs", isSignal: true, isRequired: false, transformFunction: null }, catalogNav: { classPropertyName: "catalogNav", publicName: "catalogNav", isSignal: true, isRequired: false, transformFunction: null }, showContentBack: { classPropertyName: "showContentBack", publicName: "showContentBack", isSignal: true, isRequired: false, transformFunction: null }, contentBackLink: { classPropertyName: "contentBackLink", publicName: "contentBackLink", isSignal: true, isRequired: false, transformFunction: null }, contentBackLabel: { classPropertyName: "contentBackLabel", publicName: "contentBackLabel", isSignal: true, isRequired: false, transformFunction: null }, userName: { classPropertyName: "userName", publicName: "userName", isSignal: true, isRequired: false, transformFunction: null }, greetingName: { classPropertyName: "greetingName", publicName: "greetingName", isSignal: true, isRequired: false, transformFunction: null }, userAvatarSrc: { classPropertyName: "userAvatarSrc", publicName: "userAvatarSrc", isSignal: true, isRequired: false, transformFunction: null }, userMenuItems: { classPropertyName: "userMenuItems", publicName: "userMenuItems", isSignal: true, isRequired: false, transformFunction: null }, notifications: { classPropertyName: "notifications", publicName: "notifications", isSignal: true, isRequired: false, transformFunction: null }, notificationsTitle: { classPropertyName: "notificationsTitle", publicName: "notificationsTitle", isSignal: true, isRequired: false, transformFunction: null }, showClock: { classPropertyName: "showClock", publicName: "showClock", isSignal: true, isRequired: false, transformFunction: null }, showNotifications: { classPropertyName: "showNotifications", publicName: "showNotifications", isSignal: true, isRequired: false, transformFunction: null }, clockDateFormat: { classPropertyName: "clockDateFormat", publicName: "clockDateFormat", isSignal: true, isRequired: false, transformFunction: null }, clockFormat: { classPropertyName: "clockFormat", publicName: "clockFormat", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClass()" } }, queries: [{ propertyName: "customHeader", first: true, predicate: AppShellHeaderSlotDirective, descendants: true, isSignal: true }], ngImport: i0, template: " <div [class]=\"shellRootClass()\">\n @if (mobileNavOpen()) {\n <button\n type=\"button\"\n class=\"absolute inset-0 z-40 cursor-pointer bg-ink/40\"\n [class.lg:hidden]=\"!layoutPreview()\"\n aria-label=\"Close navigation\"\n (click)=\"closeMobileNav()\"\n ></button>\n }\n\n <div [class]=\"sidenavHostClass()\">\n <ng-content select=\"[sidenav]\" />\n </div>\n\n <div class=\"flex min-w-0 flex-1 flex-col\">\n @if (showMobileBar()) {\n <div\n class=\"flex shrink-0 items-center gap-2 border-b border-border px-3 py-2 dark:border-white/10\"\n [class.lg:hidden]=\"!layoutPreview()\"\n >\n <button\n type=\"button\"\n class=\"inline-flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-lg border border-border text-ink transition-colors hover:bg-background-welcome dark:border-white/15 dark:text-white dark:hover:bg-white/10\"\n [attr.aria-label]=\"mobileNavOpen() ? 'Close navigation' : 'Open navigation'\"\n [attr.aria-expanded]=\"mobileNavOpen()\"\n (click)=\"toggleMobileNav()\"\n >\n <africanies-icon name=\"align-justify\" [size]=\"18\" />\n </button>\n <img\n [src]=\"logoMiniUrl\"\n alt=\"African Import Export Solutions\"\n class=\"h-7 w-auto object-contain\"\n />\n </div>\n }\n\n <div\n class=\"sticky top-0 z-30 border-b border-border bg-white/90 backdrop-blur-md dark:border-white/10 dark:bg-ink-950/90\"\n >\n @if (hasCustomHeader()) {\n <ng-content select=\"[africaniesAppShellHeader]\" />\n } @else if (showHeaderChrome()) {\n <africanies-app-shell-header\n [userName]=\"userName()\"\n [greetingName]=\"greetingName()\"\n [userAvatarSrc]=\"userAvatarSrc()\"\n [userMenuItems]=\"userMenuItems()\"\n [notifications]=\"notifications()\"\n [notificationsTitle]=\"notificationsTitle()\"\n [showClock]=\"showClock()\"\n [showNotifications]=\"showNotifications()\"\n [clockDateFormat]=\"clockDateFormat()\"\n [clockFormat]=\"clockFormat()\"\n [density]=\"headerDensity()\"\n />\n }\n @if (showAccent()) {\n <div\n class=\"h-0.5 w-full transition-colors duration-300\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n }\n </div>\n\n <main [class]=\"mainClass()\">\n @if (showContentChrome()) {\n <africanies-app-shell-content-header\n [breadcrumbs]=\"breadcrumbs()\"\n [title]=\"headerTitle()\"\n [subtitle]=\"headerSubtitle()\"\n [url]=\"currentUrl()\"\n [catalogNav]=\"catalogNav()\"\n [showBack]=\"showContentBack()\"\n [backLink]=\"contentBackLink()\"\n [backLabel]=\"contentBackLabel()\"\n />\n }\n <ng-content />\n </main>\n </div>\n </div>", dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "component", type: AppShellContentHeaderComponent, selector: "africanies-app-shell-content-header", inputs: ["breadcrumbs", "title", "subtitle", "url", "catalogNav", "showBack", "backLink", "backLabel"] }, { kind: "component", type: AppShellHeaderComponent, selector: "africanies-app-shell-header", inputs: ["userName", "greetingName", "userAvatarSrc", "userMenuItems", "notifications", "notificationsTitle", "onNotificationMarkRead", "onNotificationMarkAllRead", "onNotificationLoadPage", "showClock", "showNotifications", "density", "clockDateFormat", "clockFormat"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
16996
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AppShellComponent, isStandalone: true, selector: "africanies-app-shell", inputs: { contentWidth: { classPropertyName: "contentWidth", publicName: "contentWidth", isSignal: true, isRequired: false, transformFunction: null }, showAccent: { classPropertyName: "showAccent", publicName: "showAccent", isSignal: true, isRequired: false, transformFunction: null }, showHeaderChrome: { classPropertyName: "showHeaderChrome", publicName: "showHeaderChrome", isSignal: true, isRequired: false, transformFunction: null }, layoutPreview: { classPropertyName: "layoutPreview", publicName: "layoutPreview", isSignal: true, isRequired: false, transformFunction: null }, headerTitle: { classPropertyName: "headerTitle", publicName: "headerTitle", isSignal: true, isRequired: false, transformFunction: null }, headerSubtitle: { classPropertyName: "headerSubtitle", publicName: "headerSubtitle", isSignal: true, isRequired: false, transformFunction: null }, breadcrumbs: { classPropertyName: "breadcrumbs", publicName: "breadcrumbs", isSignal: true, isRequired: false, transformFunction: null }, catalogNav: { classPropertyName: "catalogNav", publicName: "catalogNav", isSignal: true, isRequired: false, transformFunction: null }, showContentBack: { classPropertyName: "showContentBack", publicName: "showContentBack", isSignal: true, isRequired: false, transformFunction: null }, contentBackLink: { classPropertyName: "contentBackLink", publicName: "contentBackLink", isSignal: true, isRequired: false, transformFunction: null }, contentBackLabel: { classPropertyName: "contentBackLabel", publicName: "contentBackLabel", isSignal: true, isRequired: false, transformFunction: null }, userName: { classPropertyName: "userName", publicName: "userName", isSignal: true, isRequired: false, transformFunction: null }, greetingName: { classPropertyName: "greetingName", publicName: "greetingName", isSignal: true, isRequired: false, transformFunction: null }, userAvatarSrc: { classPropertyName: "userAvatarSrc", publicName: "userAvatarSrc", isSignal: true, isRequired: false, transformFunction: null }, userMenuItems: { classPropertyName: "userMenuItems", publicName: "userMenuItems", isSignal: true, isRequired: false, transformFunction: null }, notifications: { classPropertyName: "notifications", publicName: "notifications", isSignal: true, isRequired: false, transformFunction: null }, notificationsTitle: { classPropertyName: "notificationsTitle", publicName: "notificationsTitle", isSignal: true, isRequired: false, transformFunction: null }, showClock: { classPropertyName: "showClock", publicName: "showClock", isSignal: true, isRequired: false, transformFunction: null }, showNotifications: { classPropertyName: "showNotifications", publicName: "showNotifications", isSignal: true, isRequired: false, transformFunction: null }, clockDateFormat: { classPropertyName: "clockDateFormat", publicName: "clockDateFormat", isSignal: true, isRequired: false, transformFunction: null }, clockFormat: { classPropertyName: "clockFormat", publicName: "clockFormat", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClass()" } }, queries: [{ propertyName: "customHeader", first: true, predicate: AppShellHeaderSlotDirective, descendants: true, isSignal: true }], ngImport: i0, template: " <div [class]=\"shellRootClass()\">\n @if (mobileNavOpen()) {\n <button\n type=\"button\"\n class=\"absolute inset-0 z-40 cursor-pointer bg-ink/40\"\n [class.lg:hidden]=\"!layoutPreview()\"\n aria-label=\"Close navigation\"\n (click)=\"closeMobileNav()\"\n ></button>\n }\n\n <div [class]=\"sidenavHostClass()\">\n <ng-content select=\"[sidenav]\" />\n </div>\n\n <div class=\"flex min-w-0 flex-1 flex-col\">\n @if (showMobileBar()) {\n <div\n class=\"flex shrink-0 items-center gap-2 border-b border-border px-3 py-2 dark:border-white/10\"\n [class.lg:hidden]=\"!layoutPreview()\"\n >\n <button\n type=\"button\"\n class=\"inline-flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-lg border border-control text-ink transition-colors hover:bg-background-hover dark:border-white/15 dark:text-white dark:hover:bg-white/10\"\n [attr.aria-label]=\"mobileNavOpen() ? 'Close navigation' : 'Open navigation'\"\n [attr.aria-expanded]=\"mobileNavOpen()\"\n (click)=\"toggleMobileNav()\"\n >\n <africanies-icon name=\"align-justify\" [size]=\"18\" />\n </button>\n <img\n [src]=\"logoMiniUrl\"\n alt=\"African Import Export Solutions\"\n class=\"h-7 w-auto object-contain\"\n />\n </div>\n }\n\n <div\n class=\"sticky top-0 z-30 border-b border-border bg-surface/90 backdrop-blur-md dark:border-white/10 dark:bg-ink-950/90\"\n >\n @if (hasCustomHeader()) {\n <ng-content select=\"[africaniesAppShellHeader]\" />\n } @else if (showHeaderChrome()) {\n <africanies-app-shell-header\n [userName]=\"userName()\"\n [greetingName]=\"greetingName()\"\n [userAvatarSrc]=\"userAvatarSrc()\"\n [userMenuItems]=\"userMenuItems()\"\n [notifications]=\"notifications()\"\n [notificationsTitle]=\"notificationsTitle()\"\n [showClock]=\"showClock()\"\n [showNotifications]=\"showNotifications()\"\n [clockDateFormat]=\"clockDateFormat()\"\n [clockFormat]=\"clockFormat()\"\n [density]=\"headerDensity()\"\n />\n }\n @if (showAccent()) {\n <div\n class=\"h-0.5 w-full transition-colors duration-300\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n }\n </div>\n\n <main [class]=\"mainClass()\">\n @if (showContentChrome()) {\n <africanies-app-shell-content-header\n [breadcrumbs]=\"breadcrumbs()\"\n [title]=\"headerTitle()\"\n [subtitle]=\"headerSubtitle()\"\n [url]=\"currentUrl()\"\n [catalogNav]=\"catalogNav()\"\n [showBack]=\"showContentBack()\"\n [backLink]=\"contentBackLink()\"\n [backLabel]=\"contentBackLabel()\"\n />\n }\n <ng-content />\n </main>\n </div>\n </div>", dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "component", type: AppShellContentHeaderComponent, selector: "africanies-app-shell-content-header", inputs: ["breadcrumbs", "title", "subtitle", "url", "catalogNav", "showBack", "backLink", "backLabel"] }, { kind: "component", type: AppShellHeaderComponent, selector: "africanies-app-shell-header", inputs: ["userName", "greetingName", "userAvatarSrc", "userMenuItems", "notifications", "notificationsTitle", "onNotificationMarkRead", "onNotificationMarkAllRead", "onNotificationLoadPage", "showClock", "showNotifications", "density", "clockDateFormat", "clockFormat"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
16901
16997
  }
16902
16998
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AppShellComponent, decorators: [{
16903
16999
  type: Component,
16904
17000
  args: [{ selector: 'africanies-app-shell', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AfricaniesIconComponent, AppShellContentHeaderComponent, AppShellHeaderComponent], host: {
16905
17001
  '[class]': 'hostClass()',
16906
- }, template: " <div [class]=\"shellRootClass()\">\n @if (mobileNavOpen()) {\n <button\n type=\"button\"\n class=\"absolute inset-0 z-40 cursor-pointer bg-ink/40\"\n [class.lg:hidden]=\"!layoutPreview()\"\n aria-label=\"Close navigation\"\n (click)=\"closeMobileNav()\"\n ></button>\n }\n\n <div [class]=\"sidenavHostClass()\">\n <ng-content select=\"[sidenav]\" />\n </div>\n\n <div class=\"flex min-w-0 flex-1 flex-col\">\n @if (showMobileBar()) {\n <div\n class=\"flex shrink-0 items-center gap-2 border-b border-border px-3 py-2 dark:border-white/10\"\n [class.lg:hidden]=\"!layoutPreview()\"\n >\n <button\n type=\"button\"\n class=\"inline-flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-lg border border-border text-ink transition-colors hover:bg-background-welcome dark:border-white/15 dark:text-white dark:hover:bg-white/10\"\n [attr.aria-label]=\"mobileNavOpen() ? 'Close navigation' : 'Open navigation'\"\n [attr.aria-expanded]=\"mobileNavOpen()\"\n (click)=\"toggleMobileNav()\"\n >\n <africanies-icon name=\"align-justify\" [size]=\"18\" />\n </button>\n <img\n [src]=\"logoMiniUrl\"\n alt=\"African Import Export Solutions\"\n class=\"h-7 w-auto object-contain\"\n />\n </div>\n }\n\n <div\n class=\"sticky top-0 z-30 border-b border-border bg-white/90 backdrop-blur-md dark:border-white/10 dark:bg-ink-950/90\"\n >\n @if (hasCustomHeader()) {\n <ng-content select=\"[africaniesAppShellHeader]\" />\n } @else if (showHeaderChrome()) {\n <africanies-app-shell-header\n [userName]=\"userName()\"\n [greetingName]=\"greetingName()\"\n [userAvatarSrc]=\"userAvatarSrc()\"\n [userMenuItems]=\"userMenuItems()\"\n [notifications]=\"notifications()\"\n [notificationsTitle]=\"notificationsTitle()\"\n [showClock]=\"showClock()\"\n [showNotifications]=\"showNotifications()\"\n [clockDateFormat]=\"clockDateFormat()\"\n [clockFormat]=\"clockFormat()\"\n [density]=\"headerDensity()\"\n />\n }\n @if (showAccent()) {\n <div\n class=\"h-0.5 w-full transition-colors duration-300\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n }\n </div>\n\n <main [class]=\"mainClass()\">\n @if (showContentChrome()) {\n <africanies-app-shell-content-header\n [breadcrumbs]=\"breadcrumbs()\"\n [title]=\"headerTitle()\"\n [subtitle]=\"headerSubtitle()\"\n [url]=\"currentUrl()\"\n [catalogNav]=\"catalogNav()\"\n [showBack]=\"showContentBack()\"\n [backLink]=\"contentBackLink()\"\n [backLabel]=\"contentBackLabel()\"\n />\n }\n <ng-content />\n </main>\n </div>\n </div>" }]
17002
+ }, template: " <div [class]=\"shellRootClass()\">\n @if (mobileNavOpen()) {\n <button\n type=\"button\"\n class=\"absolute inset-0 z-40 cursor-pointer bg-ink/40\"\n [class.lg:hidden]=\"!layoutPreview()\"\n aria-label=\"Close navigation\"\n (click)=\"closeMobileNav()\"\n ></button>\n }\n\n <div [class]=\"sidenavHostClass()\">\n <ng-content select=\"[sidenav]\" />\n </div>\n\n <div class=\"flex min-w-0 flex-1 flex-col\">\n @if (showMobileBar()) {\n <div\n class=\"flex shrink-0 items-center gap-2 border-b border-border px-3 py-2 dark:border-white/10\"\n [class.lg:hidden]=\"!layoutPreview()\"\n >\n <button\n type=\"button\"\n class=\"inline-flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-lg border border-control text-ink transition-colors hover:bg-background-hover dark:border-white/15 dark:text-white dark:hover:bg-white/10\"\n [attr.aria-label]=\"mobileNavOpen() ? 'Close navigation' : 'Open navigation'\"\n [attr.aria-expanded]=\"mobileNavOpen()\"\n (click)=\"toggleMobileNav()\"\n >\n <africanies-icon name=\"align-justify\" [size]=\"18\" />\n </button>\n <img\n [src]=\"logoMiniUrl\"\n alt=\"African Import Export Solutions\"\n class=\"h-7 w-auto object-contain\"\n />\n </div>\n }\n\n <div\n class=\"sticky top-0 z-30 border-b border-border bg-surface/90 backdrop-blur-md dark:border-white/10 dark:bg-ink-950/90\"\n >\n @if (hasCustomHeader()) {\n <ng-content select=\"[africaniesAppShellHeader]\" />\n } @else if (showHeaderChrome()) {\n <africanies-app-shell-header\n [userName]=\"userName()\"\n [greetingName]=\"greetingName()\"\n [userAvatarSrc]=\"userAvatarSrc()\"\n [userMenuItems]=\"userMenuItems()\"\n [notifications]=\"notifications()\"\n [notificationsTitle]=\"notificationsTitle()\"\n [showClock]=\"showClock()\"\n [showNotifications]=\"showNotifications()\"\n [clockDateFormat]=\"clockDateFormat()\"\n [clockFormat]=\"clockFormat()\"\n [density]=\"headerDensity()\"\n />\n }\n @if (showAccent()) {\n <div\n class=\"h-0.5 w-full transition-colors duration-300\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n }\n </div>\n\n <main [class]=\"mainClass()\">\n @if (showContentChrome()) {\n <africanies-app-shell-content-header\n [breadcrumbs]=\"breadcrumbs()\"\n [title]=\"headerTitle()\"\n [subtitle]=\"headerSubtitle()\"\n [url]=\"currentUrl()\"\n [catalogNav]=\"catalogNav()\"\n [showBack]=\"showContentBack()\"\n [backLink]=\"contentBackLink()\"\n [backLabel]=\"contentBackLabel()\"\n />\n }\n <ng-content />\n </main>\n </div>\n </div>" }]
16907
17003
  }], propDecorators: { customHeader: [{ type: i0.ContentChild, args: [i0.forwardRef(() => AppShellHeaderSlotDirective), { isSignal: true }] }], contentWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentWidth", required: false }] }], showAccent: [{ type: i0.Input, args: [{ isSignal: true, alias: "showAccent", required: false }] }], showHeaderChrome: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHeaderChrome", required: false }] }], layoutPreview: [{ type: i0.Input, args: [{ isSignal: true, alias: "layoutPreview", required: false }] }], headerTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerTitle", required: false }] }], headerSubtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerSubtitle", required: false }] }], breadcrumbs: [{ type: i0.Input, args: [{ isSignal: true, alias: "breadcrumbs", required: false }] }], catalogNav: [{ type: i0.Input, args: [{ isSignal: true, alias: "catalogNav", required: false }] }], showContentBack: [{ type: i0.Input, args: [{ isSignal: true, alias: "showContentBack", required: false }] }], contentBackLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentBackLink", required: false }] }], contentBackLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentBackLabel", required: false }] }], userName: [{ type: i0.Input, args: [{ isSignal: true, alias: "userName", required: false }] }], greetingName: [{ type: i0.Input, args: [{ isSignal: true, alias: "greetingName", required: false }] }], userAvatarSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "userAvatarSrc", required: false }] }], userMenuItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "userMenuItems", required: false }] }], notifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "notifications", required: false }] }], notificationsTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "notificationsTitle", required: false }] }], showClock: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClock", required: false }] }], showNotifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "showNotifications", required: false }] }], clockDateFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "clockDateFormat", required: false }] }], clockFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "clockFormat", required: false }] }] } });
16908
17004
 
16909
17005
  /**
@@ -17031,7 +17127,7 @@ class ImageComponent {
17031
17127
 
17032
17128
  @if (showPlaceholder()) {
17033
17129
  <div
17034
- class="absolute inset-0 flex items-center justify-center bg-neutral-200 text-neutral-500 dark:bg-white/10 dark:text-neutral-400"
17130
+ class="absolute inset-0 flex items-center justify-center bg-neutral-200 text-neutral-600 dark:bg-white/10 dark:text-neutral-400"
17035
17131
  [class.animate-pulse]="pending()"
17036
17132
  aria-hidden="true"
17037
17133
  >
@@ -17087,7 +17183,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17087
17183
 
17088
17184
  @if (showPlaceholder()) {
17089
17185
  <div
17090
- class="absolute inset-0 flex items-center justify-center bg-neutral-200 text-neutral-500 dark:bg-white/10 dark:text-neutral-400"
17186
+ class="absolute inset-0 flex items-center justify-center bg-neutral-200 text-neutral-600 dark:bg-white/10 dark:text-neutral-400"
17091
17187
  [class.animate-pulse]="pending()"
17092
17188
  aria-hidden="true"
17093
17189
  >
@@ -17431,7 +17527,7 @@ class PaginationComponent {
17431
17527
  <button
17432
17528
  africanies-button
17433
17529
  type="button"
17434
- variant="secondary"
17530
+ variant="flat"
17435
17531
  size="sm"
17436
17532
  [disabled]="controlsDisabled() || !meta().has_previous_page"
17437
17533
  (click)="emitPage(meta().current_page - 1)"
@@ -17441,7 +17537,7 @@ class PaginationComponent {
17441
17537
  @for (item of pageItems(); track trackPageItem(item, $index)) {
17442
17538
  @if (item === 'ellipsis') {
17443
17539
  <span
17444
- class="inline-flex min-w-8 items-center justify-center text-body-sm text-neutral-500 dark:text-neutral-400"
17540
+ class="inline-flex min-w-8 items-center justify-center text-body-sm text-neutral-600 dark:text-neutral-400"
17445
17541
  aria-hidden="true"
17446
17542
  >
17447
17543
 
@@ -17452,7 +17548,7 @@ class PaginationComponent {
17452
17548
  type="button"
17453
17549
  size="sm"
17454
17550
  class="min-w-8"
17455
- [variant]="item === meta().current_page ? 'primary' : 'secondary'"
17551
+ [variant]="item === meta().current_page ? 'primary' : 'flat'"
17456
17552
  [disabled]="controlsDisabled()"
17457
17553
  [attr.aria-label]="'Page ' + item"
17458
17554
  [attr.aria-current]="
@@ -17467,7 +17563,7 @@ class PaginationComponent {
17467
17563
  <button
17468
17564
  africanies-button
17469
17565
  type="button"
17470
- variant="secondary"
17566
+ variant="flat"
17471
17567
  size="sm"
17472
17568
  [disabled]="controlsDisabled() || !meta().has_next_page"
17473
17569
  (click)="emitPage(meta().current_page + 1)"
@@ -17532,7 +17628,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17532
17628
  <button
17533
17629
  africanies-button
17534
17630
  type="button"
17535
- variant="secondary"
17631
+ variant="flat"
17536
17632
  size="sm"
17537
17633
  [disabled]="controlsDisabled() || !meta().has_previous_page"
17538
17634
  (click)="emitPage(meta().current_page - 1)"
@@ -17542,7 +17638,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17542
17638
  @for (item of pageItems(); track trackPageItem(item, $index)) {
17543
17639
  @if (item === 'ellipsis') {
17544
17640
  <span
17545
- class="inline-flex min-w-8 items-center justify-center text-body-sm text-neutral-500 dark:text-neutral-400"
17641
+ class="inline-flex min-w-8 items-center justify-center text-body-sm text-neutral-600 dark:text-neutral-400"
17546
17642
  aria-hidden="true"
17547
17643
  >
17548
17644
 
@@ -17553,7 +17649,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17553
17649
  type="button"
17554
17650
  size="sm"
17555
17651
  class="min-w-8"
17556
- [variant]="item === meta().current_page ? 'primary' : 'secondary'"
17652
+ [variant]="item === meta().current_page ? 'primary' : 'flat'"
17557
17653
  [disabled]="controlsDisabled()"
17558
17654
  [attr.aria-label]="'Page ' + item"
17559
17655
  [attr.aria-current]="
@@ -17568,7 +17664,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17568
17664
  <button
17569
17665
  africanies-button
17570
17666
  type="button"
17571
- variant="secondary"
17667
+ variant="flat"
17572
17668
  size="sm"
17573
17669
  [disabled]="controlsDisabled() || !meta().has_next_page"
17574
17670
  (click)="emitPage(meta().current_page + 1)"
@@ -17592,11 +17688,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17592
17688
  * Hidden while the body shows empty or error — those states expose Retry instead.
17593
17689
  * While {@link refreshing} is true the rows stay on screen — the refresh icon
17594
17690
  * spins (do not swap to a blocking loader). Use {@link loading} when the page
17595
- * of data is changing (pagination / size): rows stay on screen and the built-in
17596
- * pager shows a circular spinner beside the page controls; the body loader
17597
- * appears only when there are no rows yet (first load) or the host cleared
17598
- * rows for a shipping-mode switch (STN SFN). Toolbar and column headers stay
17599
- * mounted.
17691
+ * of data is changing (pagination / size): rows stay on screen, a keep-rows
17692
+ * overlay covers the grid, and the built-in pager shows a circular spinner
17693
+ * beside the page controls; the body loader appears only when there are no
17694
+ * rows yet (first load) or the host cleared rows for a shipping-mode switch
17695
+ * (STN ↔ SFN). Toolbar and column headers stay mounted.
17600
17696
  * top-right **Filters** then **Export** (`showFilter` / `showExport`). The table
17601
17697
  * does not own fetch, filter, or export logic — the host handles those events.
17602
17698
  *
@@ -17655,6 +17751,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17655
17751
  * to the left when rows are expandable. Override with
17656
17752
  * {@link TableColumn.sticky}. Sticky columns use a slightly thicker bottom
17657
17753
  * border (and opaque fill) so dividers stay visible while scrolling.
17754
+ *
17755
+ * **Sticky header (desktop):** when {@link stickyHeader} is true (default),
17756
+ * large screens scroll the grid inside a capped viewport and keep `th` pinned
17757
+ * to the top of that scrollport. Mobile keeps horizontal-only overflow so
17758
+ * page scroll is unchanged.
17658
17759
  */
17659
17760
  class TableComponent {
17660
17761
  modeColor = inject(ModeColorService);
@@ -17673,6 +17774,12 @@ class TableComponent {
17673
17774
  /** Column definitions (header chrome + keys). */
17674
17775
  columns = input.required(/* @ts-ignore */
17675
17776
  ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
17777
+ /**
17778
+ * When true (default), pin column headers while scrolling the grid on
17779
+ * large screens (`lg+`). Uses a capped scrollport so sticky works with
17780
+ * horizontal overflow. Set false to keep the previous unconstrained layout.
17781
+ */
17782
+ stickyHeader = input(true, { ...(ngDevMode ? { debugName: "stickyHeader" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
17676
17783
  /**
17677
17784
  * Row records to render. Empty arrays show {@link EmptyStateComponent} in
17678
17785
  * the body unless {@link loading} or {@link error} is set. Missing `data`
@@ -17703,9 +17810,9 @@ class TableComponent {
17703
17810
  refreshing = input(false, { ...(ngDevMode ? { debugName: "refreshing" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
17704
17811
  /**
17705
17812
  * Page of data is loading (first load, shipping-mode switch, pagination,
17706
- * page size). When rows are already on screen, the pager shows a spinner
17707
- * and rows stay visible; when there are no rows (first load or the host
17708
- * dropped STN/SFN rows after a mode switch), the body shows
17813
+ * page size). When rows are already on screen, a keep-rows overlay covers
17814
+ * the grid and the pager shows a spinner; when there are no rows (first
17815
+ * load or the host dropped STN/SFN rows after a mode switch), the body shows
17709
17816
  * {@link LoadingStateComponent}. Distinct from {@link refreshing}.
17710
17817
  */
17711
17818
  loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
@@ -17954,7 +18061,18 @@ class TableComponent {
17954
18061
  'border-border whitespace-normal wrap-break-word px-[1.125rem] py-4 font-medium text-neutral-600 dark:border-white/10 dark:text-neutral-400 ' +
17955
18062
  this.headerSoftClass() +
17956
18063
  ' ' +
17957
- this.stickySurfaceClass(col, 'head'));
18064
+ this.stickySurfaceClass(col, 'head') +
18065
+ (sticky ? '' : this.headerStickClass()));
18066
+ }
18067
+ /**
18068
+ * Vertical pin for scrolling header cells on desktop. Pinned left/right
18069
+ * headers get `lg:top-0` from {@link stickyPinClass} instead.
18070
+ */
18071
+ headerStickClass() {
18072
+ if (!this.stickyHeader()) {
18073
+ return '';
18074
+ }
18075
+ return ' lg:sticky lg:top-0 lg:z-[3] print:static';
17958
18076
  }
17959
18077
  /**
17960
18078
  * Body cell classes, including sticky pin when set.
@@ -17993,9 +18111,13 @@ class TableComponent {
17993
18111
  const side = edge === 'right' ? 'sticky right-0 z-[1]' : 'sticky left-0 z-[1]';
17994
18112
  if (surface === 'head') {
17995
18113
  // Fill comes from {@link headerSoftClass} on the cell — do not re-tint.
17996
- return `${side} z-[2]`;
18114
+ // Corner pins also stick vertically on desktop when stickyHeader is on.
18115
+ const vertical = this.stickyHeader()
18116
+ ? ' z-[2] lg:top-0 lg:z-[4] print:static'
18117
+ : ' z-[2]';
18118
+ return `${side}${vertical}`;
17997
18119
  }
17998
- const fill = 'bg-white dark:bg-ink';
18120
+ const fill = 'bg-surface dark:bg-ink-surface';
17999
18121
  return `${side} ${fill}`;
18000
18122
  }
18001
18123
  /**
@@ -18101,7 +18223,7 @@ class TableComponent {
18101
18223
  this.sortChange.emit({ key, direction });
18102
18224
  }
18103
18225
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: TableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18104
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: TableComponent, isStandalone: true, selector: "africanies-table", inputs: { expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, rowTrackBy: { classPropertyName: "rowTrackBy", publicName: "rowTrackBy", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, showRefresh: { classPropertyName: "showRefresh", publicName: "showRefresh", isSignal: true, isRequired: false, transformFunction: null }, refreshLabel: { classPropertyName: "refreshLabel", publicName: "refreshLabel", isSignal: true, isRequired: false, transformFunction: null }, refreshing: { classPropertyName: "refreshing", publicName: "refreshing", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingLabel: { classPropertyName: "loadingLabel", publicName: "loadingLabel", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, showFilter: { classPropertyName: "showFilter", publicName: "showFilter", isSignal: true, isRequired: false, transformFunction: null }, filterLabel: { classPropertyName: "filterLabel", publicName: "filterLabel", isSignal: true, isRequired: false, transformFunction: null }, filterCount: { classPropertyName: "filterCount", publicName: "filterCount", isSignal: true, isRequired: false, transformFunction: null }, showExport: { classPropertyName: "showExport", publicName: "showExport", isSignal: true, isRequired: false, transformFunction: null }, exportLabel: { classPropertyName: "exportLabel", publicName: "exportLabel", isSignal: true, isRequired: false, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowExpandChange: "rowExpandChange", sortChange: "sortChange", refreshClick: "refreshClick", filterClick: "filterClick", exportClick: "exportClick", pageChange: "pageChange", sizeChange: "sizeChange" }, host: { classAttribute: "block w-full min-w-0" }, queries: [{ propertyName: "cellDefs", predicate: CellDefDirective, isSignal: true }, { propertyName: "headerCellDefs", predicate: HeaderCellDefDirective, isSignal: true }, { propertyName: "rowDetailDefs", predicate: RowDetailDefDirective, isSignal: true }], ngImport: i0, template: `
18226
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: TableComponent, isStandalone: true, selector: "africanies-table", inputs: { expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, rowTrackBy: { classPropertyName: "rowTrackBy", publicName: "rowTrackBy", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, stickyHeader: { classPropertyName: "stickyHeader", publicName: "stickyHeader", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, showRefresh: { classPropertyName: "showRefresh", publicName: "showRefresh", isSignal: true, isRequired: false, transformFunction: null }, refreshLabel: { classPropertyName: "refreshLabel", publicName: "refreshLabel", isSignal: true, isRequired: false, transformFunction: null }, refreshing: { classPropertyName: "refreshing", publicName: "refreshing", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingLabel: { classPropertyName: "loadingLabel", publicName: "loadingLabel", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, showFilter: { classPropertyName: "showFilter", publicName: "showFilter", isSignal: true, isRequired: false, transformFunction: null }, filterLabel: { classPropertyName: "filterLabel", publicName: "filterLabel", isSignal: true, isRequired: false, transformFunction: null }, filterCount: { classPropertyName: "filterCount", publicName: "filterCount", isSignal: true, isRequired: false, transformFunction: null }, showExport: { classPropertyName: "showExport", publicName: "showExport", isSignal: true, isRequired: false, transformFunction: null }, exportLabel: { classPropertyName: "exportLabel", publicName: "exportLabel", isSignal: true, isRequired: false, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowExpandChange: "rowExpandChange", sortChange: "sortChange", refreshClick: "refreshClick", filterClick: "filterClick", exportClick: "exportClick", pageChange: "pageChange", sizeChange: "sizeChange" }, host: { classAttribute: "block w-full min-w-0" }, queries: [{ propertyName: "cellDefs", predicate: CellDefDirective, isSignal: true }, { propertyName: "headerCellDefs", predicate: HeaderCellDefDirective, isSignal: true }, { propertyName: "rowDetailDefs", predicate: RowDetailDefDirective, isSignal: true }], ngImport: i0, template: `
18105
18227
  <div
18106
18228
  class="flex w-full min-w-0 flex-col gap-3"
18107
18229
  [attr.aria-busy]="loading() || refreshing() || null"
@@ -18113,7 +18235,7 @@ class TableComponent {
18113
18235
  <button
18114
18236
  africanies-button
18115
18237
  type="button"
18116
- variant="secondary"
18238
+ variant="flat"
18117
18239
  size="sm"
18118
18240
  [disabled]="loading() || refreshing()"
18119
18241
  [attr.aria-label]="refreshLabel()"
@@ -18133,7 +18255,7 @@ class TableComponent {
18133
18255
  <button
18134
18256
  africanies-button
18135
18257
  type="button"
18136
- variant="secondary"
18258
+ variant="flat"
18137
18259
  size="sm"
18138
18260
  [attr.aria-label]="filterLabel()"
18139
18261
  (click)="filterClick.emit()"
@@ -18153,7 +18275,7 @@ class TableComponent {
18153
18275
  <button
18154
18276
  africanies-button
18155
18277
  type="button"
18156
- variant="secondary"
18278
+ variant="flat"
18157
18279
  size="sm"
18158
18280
  [attr.aria-label]="exportLabel()"
18159
18281
  (click)="exportClick.emit()"
@@ -18167,19 +18289,19 @@ class TableComponent {
18167
18289
  }
18168
18290
 
18169
18291
  @if (staleError(); as staleMessage) {
18170
- <div class="flex justify-end">
18171
- <africanies-error-indicator
18172
- class="max-w-[min(100%,20rem)]"
18173
- [error]="staleMessage"
18174
- retryText="Refresh"
18175
- [refreshing]="refreshing()"
18176
- (retry)="refreshClick.emit()"
18177
- />
18178
- </div>
18292
+ <africanies-error-indicator
18293
+ class="w-full"
18294
+ [error]="staleMessage"
18295
+ retryText="Refresh"
18296
+ [refreshing]="refreshing()"
18297
+ (retry)="refreshClick.emit()"
18298
+ />
18179
18299
  }
18180
18300
 
18181
18301
  <div
18182
- class="min-w-0 w-full overflow-x-auto rounded-md border border-border bg-white dark:border-white/10 dark:bg-ink"
18302
+ class="relative min-w-0 w-full overflow-x-auto rounded-panel border border-border bg-surface dark:border-white/10 dark:bg-ink-surface"
18303
+ [class.lg:max-h-[min(70dvh,calc(100dvh-12rem))]]="stickyHeader()"
18304
+ [class.lg:overflow-auto]="stickyHeader()"
18183
18305
  >
18184
18306
  <table
18185
18307
  class="w-max min-w-full table-auto border-separate border-spacing-0 bg-inherit text-left text-body text-ink dark:text-white"
@@ -18194,7 +18316,7 @@ class TableComponent {
18194
18316
  @if (someRowsExpanded()) {
18195
18317
  <button
18196
18318
  type="button"
18197
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-white hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18319
+ class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18198
18320
  aria-label="Collapse all"
18199
18321
  (click)="collapseAllRows()"
18200
18322
  >
@@ -18203,7 +18325,7 @@ class TableComponent {
18203
18325
  } @else {
18204
18326
  <button
18205
18327
  type="button"
18206
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-white hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink disabled:cursor-not-allowed disabled:opacity-40 dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18328
+ class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus disabled:cursor-not-allowed disabled:opacity-40 dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18207
18329
  aria-label="Expand all"
18208
18330
  [disabled]="rowList().length === 0"
18209
18331
  (click)="expandAllRows()"
@@ -18217,7 +18339,8 @@ class TableComponent {
18217
18339
  <th
18218
18340
  scope="col"
18219
18341
  [class]="headerCellClass(col)"
18220
- [style.width]="col.width ?? null"
18342
+ [style.width]="col.width ?? col.maxWidth ?? null"
18343
+ [style.max-width]="col.maxWidth ?? null"
18221
18344
  >
18222
18345
  @if (headerTemplateFor(col.key); as headerTpl) {
18223
18346
  <ng-container
@@ -18226,7 +18349,7 @@ class TableComponent {
18226
18349
  } @else if (col.sortable) {
18227
18350
  <button
18228
18351
  type="button"
18229
- class="inline-flex cursor-pointer items-center gap-1 rounded-sm font-medium text-neutral-600 hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:text-white"
18352
+ class="inline-flex cursor-pointer items-center gap-1 rounded-sm font-medium text-neutral-600 hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:text-white"
18230
18353
  (click)="onSortHeaderClick(col.key)"
18231
18354
  >
18232
18355
  <span>{{ col.header }}</span>
@@ -18282,7 +18405,7 @@ class TableComponent {
18282
18405
  <td [class]="expandBodyClass(i)">
18283
18406
  <button
18284
18407
  type="button"
18285
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18408
+ class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18286
18409
  [attr.aria-label]="
18287
18410
  isRowExpanded(row, i)
18288
18411
  ? 'Collapse row details'
@@ -18301,7 +18424,8 @@ class TableComponent {
18301
18424
  @for (col of columns(); track col.key) {
18302
18425
  <td
18303
18426
  [class]="bodyCellClass(col, i)"
18304
- [style.width]="col.width ?? null"
18427
+ [style.width]="col.width ?? col.maxWidth ?? null"
18428
+ [style.max-width]="col.maxWidth ?? null"
18305
18429
  >
18306
18430
  @if (templateFor(col.key); as tpl) {
18307
18431
  <ng-container
@@ -18331,7 +18455,7 @@ class TableComponent {
18331
18455
  [class.pointer-events-none]="!isRowExpanded(row, i)"
18332
18456
  >
18333
18457
  <div
18334
- class="min-h-0 overflow-hidden border-t border-border/70 bg-neutral-50 dark:border-white/10 dark:bg-white/[0.05]"
18458
+ class="min-h-0 overflow-hidden border-t border-border-strong bg-surface-sunken dark:border-white/10 dark:bg-white/[0.05]"
18335
18459
  >
18336
18460
  <dl
18337
18461
  class="m-0 grid gap-x-6 gap-y-3 px-[1.125rem] py-4 transition-opacity duration-200 ease-out motion-reduce:transition-none sm:grid-cols-2 lg:grid-cols-3"
@@ -18341,7 +18465,7 @@ class TableComponent {
18341
18465
  @for (detail of rowDetailDefs(); track detail.label()) {
18342
18466
  <div class="min-w-0">
18343
18467
  <dt
18344
- class="m-0 text-caption font-medium text-neutral-500 dark:text-neutral-400"
18468
+ class="m-0 text-caption font-medium text-neutral-600 dark:text-neutral-400"
18345
18469
  >
18346
18470
  {{ detail.label() }}
18347
18471
  </dt>
@@ -18366,6 +18490,18 @@ class TableComponent {
18366
18490
  }
18367
18491
  </tbody>
18368
18492
  </table>
18493
+ @if (paginationLoading()) {
18494
+ <div
18495
+ class="pointer-events-none absolute inset-0 z-[5] flex items-start justify-center bg-surface/70 px-4 pt-16 dark:bg-ink-950/70"
18496
+ data-testid="africanies-table-keep-rows-loading"
18497
+ aria-hidden="true"
18498
+ >
18499
+ <africanies-loading-state
18500
+ mode="inline"
18501
+ [message]="loadingLabel()"
18502
+ />
18503
+ </div>
18504
+ }
18369
18505
  </div>
18370
18506
 
18371
18507
  @if (meta(); as pager) {
@@ -18378,7 +18514,7 @@ class TableComponent {
18378
18514
  />
18379
18515
  }
18380
18516
  </div>
18381
- `, isInline: true, styles: [":host tr.africanies-table-row:hover>td{background-color:#eef2f6}:host-context(.dark) tr.africanies-table-row:hover>td{background-color:#272729}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ButtonComponent, selector: "button[africanies-button], a[africanies-button]", inputs: ["variant", "size", "disabled", "loading"] }, { kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "component", type: EmptyStateComponent, selector: "africanies-empty-state", inputs: ["message", "refreshing"], outputs: ["retry"] }, { kind: "component", type: ErrorIndicatorComponent, selector: "africanies-error-indicator", inputs: ["error", "retryText", "refreshing", "refreshingText", "disabled"], outputs: ["retry"] }, { kind: "component", type: ErrorStateComponent, selector: "africanies-error-state", inputs: ["message", "refreshing"], outputs: ["retry"] }, { kind: "component", type: LoadingStateComponent, selector: "africanies-loading-state", inputs: ["message", "mode"] }, { kind: "component", type: PaginationComponent, selector: "africanies-pagination", inputs: ["meta", "disabled", "loading", "pageSizes"], outputs: ["pageChange", "sizeChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
18517
+ `, isInline: true, styles: [":host tr.africanies-table-row:hover>td{background-color:#e9eef5}:host-context(.dark) tr.africanies-table-row:hover>td{background-color:#272729}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ButtonComponent, selector: "button[africanies-button], a[africanies-button]", inputs: ["variant", "size", "disabled", "loading"] }, { kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "component", type: EmptyStateComponent, selector: "africanies-empty-state", inputs: ["message", "refreshing"], outputs: ["retry"] }, { kind: "component", type: ErrorIndicatorComponent, selector: "africanies-error-indicator", inputs: ["error", "retryText", "refreshing", "refreshingText", "disabled"], outputs: ["retry"] }, { kind: "component", type: ErrorStateComponent, selector: "africanies-error-state", inputs: ["message", "refreshing"], outputs: ["retry"] }, { kind: "component", type: LoadingStateComponent, selector: "africanies-loading-state", inputs: ["message", "mode"] }, { kind: "component", type: PaginationComponent, selector: "africanies-pagination", inputs: ["meta", "disabled", "loading", "pageSizes"], outputs: ["pageChange", "sizeChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
18382
18518
  }
18383
18519
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: TableComponent, decorators: [{
18384
18520
  type: Component,
@@ -18405,7 +18541,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18405
18541
  <button
18406
18542
  africanies-button
18407
18543
  type="button"
18408
- variant="secondary"
18544
+ variant="flat"
18409
18545
  size="sm"
18410
18546
  [disabled]="loading() || refreshing()"
18411
18547
  [attr.aria-label]="refreshLabel()"
@@ -18425,7 +18561,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18425
18561
  <button
18426
18562
  africanies-button
18427
18563
  type="button"
18428
- variant="secondary"
18564
+ variant="flat"
18429
18565
  size="sm"
18430
18566
  [attr.aria-label]="filterLabel()"
18431
18567
  (click)="filterClick.emit()"
@@ -18445,7 +18581,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18445
18581
  <button
18446
18582
  africanies-button
18447
18583
  type="button"
18448
- variant="secondary"
18584
+ variant="flat"
18449
18585
  size="sm"
18450
18586
  [attr.aria-label]="exportLabel()"
18451
18587
  (click)="exportClick.emit()"
@@ -18459,19 +18595,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18459
18595
  }
18460
18596
 
18461
18597
  @if (staleError(); as staleMessage) {
18462
- <div class="flex justify-end">
18463
- <africanies-error-indicator
18464
- class="max-w-[min(100%,20rem)]"
18465
- [error]="staleMessage"
18466
- retryText="Refresh"
18467
- [refreshing]="refreshing()"
18468
- (retry)="refreshClick.emit()"
18469
- />
18470
- </div>
18598
+ <africanies-error-indicator
18599
+ class="w-full"
18600
+ [error]="staleMessage"
18601
+ retryText="Refresh"
18602
+ [refreshing]="refreshing()"
18603
+ (retry)="refreshClick.emit()"
18604
+ />
18471
18605
  }
18472
18606
 
18473
18607
  <div
18474
- class="min-w-0 w-full overflow-x-auto rounded-md border border-border bg-white dark:border-white/10 dark:bg-ink"
18608
+ class="relative min-w-0 w-full overflow-x-auto rounded-panel border border-border bg-surface dark:border-white/10 dark:bg-ink-surface"
18609
+ [class.lg:max-h-[min(70dvh,calc(100dvh-12rem))]]="stickyHeader()"
18610
+ [class.lg:overflow-auto]="stickyHeader()"
18475
18611
  >
18476
18612
  <table
18477
18613
  class="w-max min-w-full table-auto border-separate border-spacing-0 bg-inherit text-left text-body text-ink dark:text-white"
@@ -18486,7 +18622,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18486
18622
  @if (someRowsExpanded()) {
18487
18623
  <button
18488
18624
  type="button"
18489
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-white hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18625
+ class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18490
18626
  aria-label="Collapse all"
18491
18627
  (click)="collapseAllRows()"
18492
18628
  >
@@ -18495,7 +18631,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18495
18631
  } @else {
18496
18632
  <button
18497
18633
  type="button"
18498
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-white hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink disabled:cursor-not-allowed disabled:opacity-40 dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18634
+ class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus disabled:cursor-not-allowed disabled:opacity-40 dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18499
18635
  aria-label="Expand all"
18500
18636
  [disabled]="rowList().length === 0"
18501
18637
  (click)="expandAllRows()"
@@ -18509,7 +18645,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18509
18645
  <th
18510
18646
  scope="col"
18511
18647
  [class]="headerCellClass(col)"
18512
- [style.width]="col.width ?? null"
18648
+ [style.width]="col.width ?? col.maxWidth ?? null"
18649
+ [style.max-width]="col.maxWidth ?? null"
18513
18650
  >
18514
18651
  @if (headerTemplateFor(col.key); as headerTpl) {
18515
18652
  <ng-container
@@ -18518,7 +18655,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18518
18655
  } @else if (col.sortable) {
18519
18656
  <button
18520
18657
  type="button"
18521
- class="inline-flex cursor-pointer items-center gap-1 rounded-sm font-medium text-neutral-600 hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:text-white"
18658
+ class="inline-flex cursor-pointer items-center gap-1 rounded-sm font-medium text-neutral-600 hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:text-white"
18522
18659
  (click)="onSortHeaderClick(col.key)"
18523
18660
  >
18524
18661
  <span>{{ col.header }}</span>
@@ -18574,7 +18711,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18574
18711
  <td [class]="expandBodyClass(i)">
18575
18712
  <button
18576
18713
  type="button"
18577
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18714
+ class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18578
18715
  [attr.aria-label]="
18579
18716
  isRowExpanded(row, i)
18580
18717
  ? 'Collapse row details'
@@ -18593,7 +18730,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18593
18730
  @for (col of columns(); track col.key) {
18594
18731
  <td
18595
18732
  [class]="bodyCellClass(col, i)"
18596
- [style.width]="col.width ?? null"
18733
+ [style.width]="col.width ?? col.maxWidth ?? null"
18734
+ [style.max-width]="col.maxWidth ?? null"
18597
18735
  >
18598
18736
  @if (templateFor(col.key); as tpl) {
18599
18737
  <ng-container
@@ -18623,7 +18761,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18623
18761
  [class.pointer-events-none]="!isRowExpanded(row, i)"
18624
18762
  >
18625
18763
  <div
18626
- class="min-h-0 overflow-hidden border-t border-border/70 bg-neutral-50 dark:border-white/10 dark:bg-white/[0.05]"
18764
+ class="min-h-0 overflow-hidden border-t border-border-strong bg-surface-sunken dark:border-white/10 dark:bg-white/[0.05]"
18627
18765
  >
18628
18766
  <dl
18629
18767
  class="m-0 grid gap-x-6 gap-y-3 px-[1.125rem] py-4 transition-opacity duration-200 ease-out motion-reduce:transition-none sm:grid-cols-2 lg:grid-cols-3"
@@ -18633,7 +18771,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18633
18771
  @for (detail of rowDetailDefs(); track detail.label()) {
18634
18772
  <div class="min-w-0">
18635
18773
  <dt
18636
- class="m-0 text-caption font-medium text-neutral-500 dark:text-neutral-400"
18774
+ class="m-0 text-caption font-medium text-neutral-600 dark:text-neutral-400"
18637
18775
  >
18638
18776
  {{ detail.label() }}
18639
18777
  </dt>
@@ -18658,6 +18796,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18658
18796
  }
18659
18797
  </tbody>
18660
18798
  </table>
18799
+ @if (paginationLoading()) {
18800
+ <div
18801
+ class="pointer-events-none absolute inset-0 z-[5] flex items-start justify-center bg-surface/70 px-4 pt-16 dark:bg-ink-950/70"
18802
+ data-testid="africanies-table-keep-rows-loading"
18803
+ aria-hidden="true"
18804
+ >
18805
+ <africanies-loading-state
18806
+ mode="inline"
18807
+ [message]="loadingLabel()"
18808
+ />
18809
+ </div>
18810
+ }
18661
18811
  </div>
18662
18812
 
18663
18813
  @if (meta(); as pager) {
@@ -18670,8 +18820,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18670
18820
  />
18671
18821
  }
18672
18822
  </div>
18673
- `, styles: [":host tr.africanies-table-row:hover>td{background-color:#eef2f6}:host-context(.dark) tr.africanies-table-row:hover>td{background-color:#272729}\n"] }]
18674
- }], propDecorators: { expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], rowTrackBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowTrackBy", required: false }] }], rowExpandChange: [{ type: i0.Output, args: ["rowExpandChange"] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }], showRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRefresh", required: false }] }], refreshLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "refreshLabel", required: false }] }], refreshing: [{ type: i0.Input, args: [{ isSignal: true, alias: "refreshing", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], loadingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingLabel", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], showFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFilter", required: false }] }], filterLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterLabel", required: false }] }], filterCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterCount", required: false }] }], showExport: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExport", required: false }] }], exportLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportLabel", required: false }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], refreshClick: [{ type: i0.Output, args: ["refreshClick"] }], filterClick: [{ type: i0.Output, args: ["filterClick"] }], exportClick: [{ type: i0.Output, args: ["exportClick"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], sizeChange: [{ type: i0.Output, args: ["sizeChange"] }], cellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => CellDefDirective), { isSignal: true }] }], headerCellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => HeaderCellDefDirective), { isSignal: true }] }], rowDetailDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => RowDetailDefDirective), { isSignal: true }] }] } });
18823
+ `, styles: [":host tr.africanies-table-row:hover>td{background-color:#e9eef5}:host-context(.dark) tr.africanies-table-row:hover>td{background-color:#272729}\n"] }]
18824
+ }], propDecorators: { expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], rowTrackBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowTrackBy", required: false }] }], rowExpandChange: [{ type: i0.Output, args: ["rowExpandChange"] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], stickyHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "stickyHeader", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }], showRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRefresh", required: false }] }], refreshLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "refreshLabel", required: false }] }], refreshing: [{ type: i0.Input, args: [{ isSignal: true, alias: "refreshing", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], loadingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingLabel", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], showFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFilter", required: false }] }], filterLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterLabel", required: false }] }], filterCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterCount", required: false }] }], showExport: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExport", required: false }] }], exportLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportLabel", required: false }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], refreshClick: [{ type: i0.Output, args: ["refreshClick"] }], filterClick: [{ type: i0.Output, args: ["filterClick"] }], exportClick: [{ type: i0.Output, args: ["exportClick"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], sizeChange: [{ type: i0.Output, args: ["sizeChange"] }], cellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => CellDefDirective), { isSignal: true }] }], headerCellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => HeaderCellDefDirective), { isSignal: true }] }], rowDetailDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => RowDetailDefDirective), { isSignal: true }] }] } });
18675
18825
 
18676
18826
  /**
18677
18827
  * Presentational table with template-based cells.
@@ -18817,7 +18967,7 @@ class StepperComponent {
18817
18967
  if (past) {
18818
18968
  return 'cursor-pointer text-ink hover:bg-background-welcome dark:text-white dark:hover:bg-white/10';
18819
18969
  }
18820
- return 'opacity-40 text-neutral-500 dark:text-neutral-400';
18970
+ return 'opacity-40 text-neutral-600 dark:text-neutral-400';
18821
18971
  }
18822
18972
  stepBadgeClass(index) {
18823
18973
  const base = 'inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full border text-body-sm font-medium';
@@ -18825,7 +18975,7 @@ class StepperComponent {
18825
18975
  const c = this.modeColor.classes();
18826
18976
  return `${base} ${c.border} ${c.bg} text-white`;
18827
18977
  }
18828
- return `${base} border-border bg-white text-neutral-500 dark:border-white/20 dark:bg-ink-950 dark:text-neutral-400`;
18978
+ return `${base} border-control bg-surface text-neutral-600 dark:border-white/20 dark:bg-ink-950 dark:text-neutral-400`;
18829
18979
  }
18830
18980
  canActivate(index) {
18831
18981
  if (index === this.activeIndex()) {
@@ -18850,7 +19000,7 @@ class StepperComponent {
18850
19000
  <div class="flex flex-col gap-5 text-ink dark:text-white">
18851
19001
  <div class="flex flex-col gap-2">
18852
19002
  <p
18853
- class="m-0 text-caption font-medium uppercase tracking-wide text-neutral-500 dark:text-neutral-400"
19003
+ class="m-0 text-caption font-medium uppercase tracking-wide text-neutral-600 dark:text-neutral-400"
18854
19004
  >
18855
19005
  Step {{ activeIndex() + 1 }} of {{ steps().length }}
18856
19006
  <span
@@ -18879,7 +19029,7 @@ class StepperComponent {
18879
19029
  <li class="flex min-w-0 shrink-0 items-center gap-1">
18880
19030
  <button
18881
19031
  type="button"
18882
- class="inline-flex max-w-38 items-center gap-2 rounded-lg px-2 py-1.5 text-left transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink disabled:cursor-not-allowed"
19032
+ class="inline-flex max-w-38 items-center gap-2 rounded-lg px-2 py-1.5 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ink disabled:cursor-not-allowed"
18883
19033
  [class]="headerButtonClass(i)"
18884
19034
  [attr.aria-current]="i === activeIndex() ? 'step' : null"
18885
19035
  [disabled]="!canActivate(i)"
@@ -18918,7 +19068,7 @@ class StepperComponent {
18918
19068
  }
18919
19069
  </div>
18920
19070
  </div>
18921
- `, isInline: true, styles: [".africanies-step-rail{scrollbar-width:thin}.africanies-step-body{min-width:0;overflow-x:clip;padding-inline:2px;margin-inline:-2px}.africanies-step-pane{min-width:0;will-change:opacity,transform}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
19071
+ `, isInline: true, styles: [".africanies-step-rail{scrollbar-width:thin}.africanies-step-body{min-width:0;overflow-x:clip;padding-inline:4px;margin-inline:-4px}.africanies-step-pane{min-width:0;will-change:opacity,transform}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
18922
19072
  }
18923
19073
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: StepperComponent, decorators: [{
18924
19074
  type: Component,
@@ -18926,7 +19076,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18926
19076
  <div class="flex flex-col gap-5 text-ink dark:text-white">
18927
19077
  <div class="flex flex-col gap-2">
18928
19078
  <p
18929
- class="m-0 text-caption font-medium uppercase tracking-wide text-neutral-500 dark:text-neutral-400"
19079
+ class="m-0 text-caption font-medium uppercase tracking-wide text-neutral-600 dark:text-neutral-400"
18930
19080
  >
18931
19081
  Step {{ activeIndex() + 1 }} of {{ steps().length }}
18932
19082
  <span
@@ -18955,7 +19105,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18955
19105
  <li class="flex min-w-0 shrink-0 items-center gap-1">
18956
19106
  <button
18957
19107
  type="button"
18958
- class="inline-flex max-w-38 items-center gap-2 rounded-lg px-2 py-1.5 text-left transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink disabled:cursor-not-allowed"
19108
+ class="inline-flex max-w-38 items-center gap-2 rounded-lg px-2 py-1.5 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ink disabled:cursor-not-allowed"
18959
19109
  [class]="headerButtonClass(i)"
18960
19110
  [attr.aria-current]="i === activeIndex() ? 'step' : null"
18961
19111
  [disabled]="!canActivate(i)"
@@ -18994,7 +19144,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18994
19144
  }
18995
19145
  </div>
18996
19146
  </div>
18997
- `, styles: [".africanies-step-rail{scrollbar-width:thin}.africanies-step-body{min-width:0;overflow-x:clip;padding-inline:2px;margin-inline:-2px}.africanies-step-pane{min-width:0;will-change:opacity,transform}\n"] }]
19147
+ `, styles: [".africanies-step-rail{scrollbar-width:thin}.africanies-step-body{min-width:0;overflow-x:clip;padding-inline:4px;margin-inline:-4px}.africanies-step-pane{min-width:0;will-change:opacity,transform}\n"] }]
18998
19148
  }], ctorParameters: () => [], propDecorators: { steps: [{ type: i0.Input, args: [{ isSignal: true, alias: "steps", required: true }] }], activeIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeIndex", required: true }] }], linear: [{ type: i0.Input, args: [{ isSignal: true, alias: "linear", required: false }] }], animateSteps: [{ type: i0.Input, args: [{ isSignal: true, alias: "animateSteps", required: false }] }], activeIndexChange: [{ type: i0.Output, args: ["activeIndexChange"] }], stepDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => StepDefDirective), { isSignal: true }] }], stepPane: [{ type: i0.ViewChild, args: ['stepPane', { isSignal: true }] }] } });
18999
19149
 
19000
19150
  /**