@africanies/angular-web-sdk 0.1.5 → 0.1.7

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 (142) hide show
  1. package/.nx/cache/{13524438527920092277 → 13466509747676776487}/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +140 -52
  2. package/.nx/cache/13466509747676776487/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +1 -0
  3. package/.nx/cache/{13524438527920092277 → 13466509747676776487}/dist/libs/africanies-core/package.json +3 -3
  4. package/.nx/cache/{13524438527920092277 → 13466509747676776487}/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +14 -11
  5. package/.nx/cache/{18132311043100376919 → 14995846848874973576}/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +539 -232
  6. package/.nx/cache/14995846848874973576/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -0
  7. package/.nx/cache/{18132311043100376919 → 14995846848874973576}/dist/libs/africanies-ui/package.json +5 -5
  8. package/.nx/cache/{5741611231803799383 → 14995846848874973576}/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +111 -28
  9. package/.nx/cache/{1275815545780424169 → 6673686859704879504}/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +16 -0
  10. package/.nx/cache/6673686859704879504/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -0
  11. package/.nx/cache/{14153928176747262656 → 6673686859704879504}/dist/libs/africanies-theme/package.json +3 -3
  12. package/.nx/cache/{1275815545780424169 → 6673686859704879504}/dist/libs/africanies-theme/tailwind-preset.cjs +31 -14
  13. package/.nx/cache/{14153928176747262656 → 6673686859704879504}/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +18 -1
  14. package/.nx/cache/run.json +18 -18
  15. package/.nx/cache/terminalOutputs/{4011892767661954819 → 13466509747676776487} +1 -1
  16. package/.nx/cache/terminalOutputs/1446890025183346611 +32 -0
  17. package/.nx/cache/terminalOutputs/{3064561615116090625 → 14995846848874973576} +7 -7
  18. package/.nx/cache/terminalOutputs/6337357866566791975 +6 -0
  19. package/.nx/cache/terminalOutputs/{1275815545780424169 → 6673686859704879504} +1 -1
  20. package/.nx/cache/terminalOutputs/{12148417737220755248 → 8032148447904822353} +182 -174
  21. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db +0 -0
  22. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-shm +0 -0
  23. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-wal +0 -0
  24. package/.nx/workspace-data/d/daemon.log +2461 -0
  25. package/.nx/workspace-data/d/server-process.json +2 -2
  26. package/.nx/workspace-data/file-map.json +2747 -2747
  27. package/.nx/workspace-data/nx_files.nxt +0 -0
  28. package/.nx/workspace-data/project-graph.json +2 -2
  29. package/apps/playground/src/app/pages/button-page.ts +5 -3
  30. package/apps/playground/src/app/pages/feedback-page.ts +1 -1
  31. package/apps/playground/src/app/pages/home-page.ts +1 -1
  32. package/apps/playground/src/app/pages/icons-page.ts +3 -3
  33. package/apps/playground/src/app/pages/lecture-page.ts +3 -3
  34. package/apps/playground/src/app/pages/models-page.ts +1 -1
  35. package/apps/playground/src/app/pages/overlay-demos.ts +1 -1
  36. package/apps/playground/src/app/pages/tokens-page.ts +17 -3
  37. package/apps/playground/src/app/pages/usecases/onboarding-login.page.ts +1 -1
  38. package/apps/playground/src/app/pages/usecases/onboarding-reset-password.page.ts +1 -1
  39. package/apps/playground/src/app/pages/usecases/shipment-detail.page.ts +1 -1
  40. package/apps/playground/src/app/pages/viewport-preview.component.ts +1 -1
  41. package/apps/playground/src/app/snippets/button.snippets.ts +4 -3
  42. package/apps/playground/src/index.html +6 -0
  43. package/apps/playground/src/styles.css +4 -5
  44. package/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +131 -8
  45. package/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +1 -1
  46. package/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +8 -6
  47. package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +12 -2
  48. package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -1
  49. package/dist/libs/africanies-theme/tailwind-preset.cjs +16 -7
  50. package/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +11 -3
  51. package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +83 -63
  52. package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -1
  53. package/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +11 -8
  54. package/libs/africanies-core/src/index.ts +1 -0
  55. package/libs/africanies-core/src/lib/notification/index.ts +1 -1
  56. package/libs/africanies-core/src/lib/notification/notification-link.spec.ts +40 -7
  57. package/libs/africanies-core/src/lib/notification/notification-link.ts +143 -7
  58. package/libs/africanies-theme/src/lib/mode-color.safelist.ts +8 -2
  59. package/libs/africanies-theme/src/lib/mode-color.service.ts +16 -0
  60. package/libs/africanies-theme/tailwind-preset.cjs +16 -7
  61. package/libs/africanies-ui/src/lib/button/button.component.ts +5 -1
  62. package/libs/africanies-ui/src/lib/feedback/empty-state.component.ts +1 -1
  63. package/libs/africanies-ui/src/lib/feedback/error-state.component.ts +1 -1
  64. package/libs/africanies-ui/src/lib/forms/file-upload/file-preview.dialog.ts +1 -1
  65. package/libs/africanies-ui/src/lib/forms/file-upload/file-upload.component.ts +4 -4
  66. package/libs/africanies-ui/src/lib/forms/form-field.classes.ts +2 -2
  67. package/libs/africanies-ui/src/lib/forms/select/select.component.ts +1 -1
  68. package/libs/africanies-ui/src/lib/layout/app-shell-header.component.ts +1 -1
  69. package/libs/africanies-ui/src/lib/layout/app-shell.component.ts +2 -2
  70. package/libs/africanies-ui/src/lib/navigation/shipping-mode-switch/shipping-mode-switch.component.ts +2 -2
  71. package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.html +2 -2
  72. package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.ts +10 -6
  73. package/libs/africanies-ui/src/lib/notifications/notification-drawer.panel.ts +21 -8
  74. package/libs/africanies-ui/src/lib/overlay/drawer.service.ts +1 -1
  75. package/libs/africanies-ui/src/lib/overlay/modal.service.ts +1 -1
  76. package/libs/africanies-ui/src/lib/pagination/pagination.component.ts +3 -3
  77. package/libs/africanies-ui/src/lib/table/table.component.ts +5 -5
  78. package/libs/africanies-ui/src/lib/toast/toast-host.component.ts +4 -4
  79. package/libs/africanies-ui/src/lib/toast/toast-item.component.ts +1 -1
  80. package/package.json +1 -1
  81. package/.nx/cache/1275815545780424169/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +0 -1
  82. package/.nx/cache/1275815545780424169/dist/libs/africanies-theme/package.json +0 -36
  83. package/.nx/cache/1275815545780424169/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +0 -157
  84. package/.nx/cache/13524438527920092277/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +0 -1
  85. package/.nx/cache/14153928176747262656/dist/libs/africanies-theme/README.md +0 -46
  86. package/.nx/cache/14153928176747262656/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +0 -215
  87. package/.nx/cache/14153928176747262656/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +0 -1
  88. package/.nx/cache/14153928176747262656/dist/libs/africanies-theme/tailwind-preset.cjs +0 -245
  89. package/.nx/cache/18132311043100376919/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +0 -1
  90. package/.nx/cache/18132311043100376919/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +0 -5410
  91. package/.nx/cache/2782605790118376385/dist/libs/africanies-icons/README.md +0 -69
  92. package/.nx/cache/2782605790118376385/dist/libs/africanies-icons/assets/icons.sprite.svg +0 -1844
  93. package/.nx/cache/2782605790118376385/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs +0 -832
  94. package/.nx/cache/2782605790118376385/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs.map +0 -1
  95. package/.nx/cache/2782605790118376385/dist/libs/africanies-icons/package.json +0 -31
  96. package/.nx/cache/2782605790118376385/dist/libs/africanies-icons/types/africanies-africanies-icons.d.ts +0 -108
  97. package/.nx/cache/3064561615116090625/dist/libs/africanies-storage/README.md +0 -31
  98. package/.nx/cache/3064561615116090625/dist/libs/africanies-storage/fesm2022/africanies-africanies-storage.mjs +0 -243
  99. package/.nx/cache/3064561615116090625/dist/libs/africanies-storage/fesm2022/africanies-africanies-storage.mjs.map +0 -1
  100. package/.nx/cache/3064561615116090625/dist/libs/africanies-storage/package.json +0 -27
  101. package/.nx/cache/3064561615116090625/dist/libs/africanies-storage/types/africanies-africanies-storage.d.ts +0 -210
  102. package/.nx/cache/4011892767661954819/dist/libs/africanies-core/README.md +0 -85
  103. package/.nx/cache/4011892767661954819/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +0 -4546
  104. package/.nx/cache/4011892767661954819/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +0 -1
  105. package/.nx/cache/4011892767661954819/dist/libs/africanies-core/package.json +0 -32
  106. package/.nx/cache/4011892767661954819/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +0 -2740
  107. package/.nx/cache/5741611231803799383/dist/libs/africanies-ui/README.md +0 -68
  108. package/.nx/cache/5741611231803799383/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
  109. package/.nx/cache/5741611231803799383/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
  110. package/.nx/cache/5741611231803799383/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +0 -5
  111. package/.nx/cache/5741611231803799383/dist/libs/africanies-ui/assets/carriers/dhl.svg +0 -7
  112. package/.nx/cache/5741611231803799383/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +0 -19088
  113. package/.nx/cache/5741611231803799383/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +0 -1
  114. package/.nx/cache/5741611231803799383/dist/libs/africanies-ui/package.json +0 -36
  115. package/.nx/cache/6372470950255467653/dist/libs/africanies-models/README.md +0 -60
  116. package/.nx/cache/6372470950255467653/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs +0 -599
  117. package/.nx/cache/6372470950255467653/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs.map +0 -1
  118. package/.nx/cache/6372470950255467653/dist/libs/africanies-models/package.json +0 -24
  119. package/.nx/cache/6372470950255467653/dist/libs/africanies-models/types/africanies-africanies-models.d.ts +0 -1452
  120. package/.nx/cache/terminalOutputs/11768036696192889608 +0 -8
  121. package/.nx/cache/terminalOutputs/1227201614544602906 +0 -23
  122. package/.nx/cache/terminalOutputs/12538607787148205787 +0 -19
  123. package/.nx/cache/terminalOutputs/13524438527920092277 +0 -196
  124. package/.nx/cache/terminalOutputs/14153928176747262656 +0 -45
  125. package/.nx/cache/terminalOutputs/17404566492103808557 +0 -8
  126. package/.nx/cache/terminalOutputs/18132311043100376919 +0 -315
  127. package/.nx/cache/terminalOutputs/2436781291243085315 +0 -8
  128. package/.nx/cache/terminalOutputs/2782605790118376385 +0 -21
  129. package/.nx/cache/terminalOutputs/3681975492957101833 +0 -8
  130. package/.nx/cache/terminalOutputs/5741611231803799383 +0 -21
  131. package/.nx/cache/terminalOutputs/6037166070823946880 +0 -8
  132. package/.nx/cache/terminalOutputs/6372470950255467653 +0 -21
  133. package/.nx/cache/terminalOutputs/665190206482526466 +0 -175
  134. package/.nx/cache/terminalOutputs/7756595539965457927 +0 -23
  135. package/.nx/cache/terminalOutputs/9200013119269230075 +0 -8
  136. /package/.nx/cache/{13524438527920092277 → 13466509747676776487}/dist/libs/africanies-core/README.md +0 -0
  137. /package/.nx/cache/{18132311043100376919 → 14995846848874973576}/dist/libs/africanies-ui/README.md +0 -0
  138. /package/.nx/cache/{18132311043100376919 → 14995846848874973576}/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
  139. /package/.nx/cache/{18132311043100376919 → 14995846848874973576}/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
  140. /package/.nx/cache/{18132311043100376919 → 14995846848874973576}/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +0 -0
  141. /package/.nx/cache/{18132311043100376919 → 14995846848874973576}/dist/libs/africanies-ui/assets/carriers/dhl.svg +0 -0
  142. /package/.nx/cache/{1275815545780424169 → 6673686859704879504}/dist/libs/africanies-theme/README.md +0 -0
@@ -1,17 +1,17 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, input, booleanAttribute, computed, ChangeDetectionStrategy, Component, output, signal, Injector, DestroyRef, Injectable, numberAttribute, Directive, viewChild, contentChild, makeEnvironmentProviders, provideAppInitializer, model, forwardRef, InjectionToken, effect, afterNextRender, viewChildren, TemplateRef, contentChildren, untracked, runInInjectionContext, afterRenderEffect, NgModule } from '@angular/core';
2
+ import { inject, input, booleanAttribute, computed, ChangeDetectionStrategy, Component, output, signal, Injector, DestroyRef, Injectable, numberAttribute, Directive, viewChild, contentChild, model, makeEnvironmentProviders, provideAppInitializer, forwardRef, InjectionToken, effect, afterNextRender, viewChildren, TemplateRef, contentChildren, untracked, runInInjectionContext, afterRenderEffect, NgModule } from '@angular/core';
3
3
  import { AfricaniesIconComponent } from '@africanies/africanies-icons';
4
4
  import { ModeColorService, ThemeService } from '@africanies/africanies-theme';
5
5
  import { copyToClipboard, AFRICANIES_HTTP_TOAST, FilterOptionsResolver, MODAL_SERVICE, DRAWER_SERVICE, ShippingModeService, resolveNotificationLinkForMode } from '@africanies/africanies-core';
6
6
  import { Overlay, CdkOverlayOrigin, CdkConnectedOverlay } from '@angular/cdk/overlay';
7
7
  import { ComponentPortal } from '@angular/cdk/portal';
8
+ import { Router, ActivatedRoute, RouterLink, NavigationEnd } from '@angular/router';
8
9
  import { EXPORT_DELIVERY_VENDORS, DELIVERY_VENDORS, normalizeDeliveryVendorForForm, isKnownDeliveryVendor, hasFilterParams, fromFilterParams, filterQueryKeys, toFilterParams, cloneFilterState, resetFilterState, PAGINATION_PAGE_SIZES, DEFAULT_PAGE_SIZE } from '@africanies/africanies-models';
9
10
  export { DEFAULT_FILTER_TRANSPORT, DEFAULT_PAGE_SIZE, FILTER_CONFIGS, FilterTransport, NOTIFICATION_PAGE_SIZE, PAGINATION_PAGE_SIZES, clearFilterField, cloneFilterState, emptyFilterState, filterQueryKeys, fromFilterParams, hasFilterParams, resetFilterState, resolveFilterTransport, shipmentTrackingItemFilterConfig, toFilterParams, trackShipmentsFilterConfig, updateShipmentsFilterConfig, usersFilterConfig } from '@africanies/africanies-models';
10
11
  import { HttpErrorResponse } from '@angular/common/http';
11
12
  import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
12
13
  import { Subject, take, isObservable, defer, throwError, of, from, map, timer, filter, startWith } from 'rxjs';
13
14
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
14
- import { ActivatedRoute, Router, RouterLink, NavigationEnd } from '@angular/router';
15
15
  import { debounce, switchMap, distinctUntilChanged, tap, catchError } from 'rxjs/operators';
16
16
  import { NgTemplateOutlet, NgClass, DatePipe } from '@angular/common';
17
17
 
@@ -108,6 +108,9 @@ 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
115
  'bg-white dark:bg-ink-950 text-ink dark:text-white border-border dark:border-white/15 hover:bg-background-welcome dark:hover:bg-white/10';
113
116
  }
@@ -270,7 +273,7 @@ class ToastItemComponent {
270
273
  ...(ngDevMode ? [{ debugName: "a11yLive" }] : /* istanbul ignore next */ []));
271
274
  shellClass = computed(() => 'africanies-toast-enter relative flex w-full overflow-hidden rounded-lg border border-border ' +
272
275
  'bg-white/95 text-ink shadow-lg backdrop-blur-md ' +
273
- 'dark:border-white/15 dark:bg-ink/95 dark:text-white', /* @ts-ignore */
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 */ []));
@@ -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()"
@@ -1303,6 +1306,84 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
1303
1306
  * Clipboard copy control (`africanies-copy`).
1304
1307
  */
1305
1308
 
1309
+ /**
1310
+ * Builds a {@link UrlTree} for an item’s `routerLink` / query / fragment.
1311
+ *
1312
+ * @param router - Consumer app router.
1313
+ * @param item - Nav item with a `routerLink`.
1314
+ * @returns UrlTree, or `null` when the item is not routed.
1315
+ */
1316
+ function navItemUrlTree(router, item) {
1317
+ if (item.routerLink == null) {
1318
+ return null;
1319
+ }
1320
+ const commands = Array.isArray(item.routerLink)
1321
+ ? [...item.routerLink]
1322
+ : [item.routerLink];
1323
+ return router.createUrlTree(commands, {
1324
+ queryParams: item.queryParams,
1325
+ fragment: item.fragment,
1326
+ });
1327
+ }
1328
+ /**
1329
+ * Whether the current router URL matches this item (cold load + navigations).
1330
+ *
1331
+ * @param router - Consumer app router.
1332
+ * @param item - Nav item with a `routerLink`.
1333
+ * @param options - Same shape as `RouterLinkActive` options.
1334
+ * @returns True when the current URL matches the item.
1335
+ */
1336
+ function isNavItemActive(router, item, options) {
1337
+ const tree = navItemUrlTree(router, item);
1338
+ if (!tree) {
1339
+ return false;
1340
+ }
1341
+ return router.isActive(tree, options);
1342
+ }
1343
+
1344
+ /**
1345
+ * Whether a click should leave navigation to the browser (new tab, etc.).
1346
+ * @param event - Mouse click from a routed nav control.
1347
+ * @returns True when the host should not call `preventDefault` / navigate.
1348
+ */
1349
+ function isModifiedClick(event) {
1350
+ return (event.button !== 0 ||
1351
+ event.metaKey ||
1352
+ event.ctrlKey ||
1353
+ event.shiftKey ||
1354
+ event.altKey);
1355
+ }
1356
+ /**
1357
+ * Serialized href for a routed nav item (for `<a href>` without RouterLink).
1358
+ * @param router - App router.
1359
+ * @param item - Nav item with `routerLink`.
1360
+ * @returns Path string, or `null` when the item is not routed.
1361
+ */
1362
+ function navItemHref(router, item) {
1363
+ const tree = navItemUrlTree(router, item);
1364
+ return tree ? router.serializeUrl(tree) : null;
1365
+ }
1366
+ /**
1367
+ * Navigate to a nav item without jumping the viewport to the top.
1368
+ *
1369
+ * Apps that enable `withInMemoryScrolling({ scrollPositionRestoration:
1370
+ * 'enabled' })` scroll to `[0, 0]` on every forward navigation. That is wrong
1371
+ * for in-page tabs/segments. Passing `{ scroll: 'manual' }` tells Angular's
1372
+ * RouterScroller to skip scrolling for this navigation.
1373
+ *
1374
+ * @param router - App router.
1375
+ * @param item - Nav item with `routerLink`.
1376
+ * @param keepScroll - When true (default for tabs/segments), skip router scroll.
1377
+ * @returns Navigation promise, or `null` when the item is not routed.
1378
+ */
1379
+ function navigateNavItem(router, item, keepScroll = true) {
1380
+ const tree = navItemUrlTree(router, item);
1381
+ if (!tree) {
1382
+ return null;
1383
+ }
1384
+ return router.navigateByUrl(tree, keepScroll ? { scroll: 'manual' } : undefined);
1385
+ }
1386
+
1306
1387
  /**
1307
1388
  * Marks projected content as the custom trigger for {@link ActionMenuComponent}.
1308
1389
  *
@@ -1358,6 +1439,9 @@ const MENU_PANEL_POSITIONS = [
1358
1439
  * clipped by table `overflow`. Default trigger is a ghost ellipsis button;
1359
1440
  * project a custom control with {@link ActionMenuTriggerDirective}.
1360
1441
  *
1442
+ * Items with {@link AfricaniesMenuItem.routerLink} render as anchors so users
1443
+ * can right-click / modified-click to open in a new tab.
1444
+ *
1361
1445
  * @example
1362
1446
  * ```html
1363
1447
  * <!-- Default icon trigger (table row) -->
@@ -1373,7 +1457,12 @@ const MENU_PANEL_POSITIONS = [
1373
1457
  * ```ts
1374
1458
  * rowActions(row: Shipment): AfricaniesMenuItem[] {
1375
1459
  * return [
1376
- * { label: 'Open', icon: 'eye', onClick: () => this.open(row) },
1460
+ * {
1461
+ * label: 'Open',
1462
+ * icon: 'eye',
1463
+ * routerLink: ['/shipments', row.id],
1464
+ * onClick: () => this.open(row),
1465
+ * },
1377
1466
  * { label: 'Edit', icon: 'edit', onClick: () => this.edit(row) },
1378
1467
  * {
1379
1468
  * label: 'Delete',
@@ -1387,6 +1476,7 @@ const MENU_PANEL_POSITIONS = [
1387
1476
  * ```
1388
1477
  */
1389
1478
  class ActionMenuComponent {
1479
+ router = inject(Router);
1390
1480
  panelPositions = MENU_PANEL_POSITIONS;
1391
1481
  panelRef = viewChild('panel', /* @ts-ignore */
1392
1482
  ...(ngDevMode ? [{ debugName: "panelRef" }] : /* istanbul ignore next */ []));
@@ -1412,7 +1502,7 @@ class ActionMenuComponent {
1412
1502
  }
1413
1503
  itemClass(item, index) {
1414
1504
  const active = this.activeIndex() === index;
1415
- const base = 'flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-body-sm transition-colors ' +
1505
+ const base = 'flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-body-sm no-underline transition-colors ' +
1416
1506
  'disabled:cursor-not-allowed disabled:opacity-50 ' +
1417
1507
  'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ink';
1418
1508
  if (item.danger) {
@@ -1426,6 +1516,9 @@ class ActionMenuComponent {
1426
1516
  ? 'bg-background-welcome dark:bg-white/10'
1427
1517
  : 'hover:bg-background-welcome dark:hover:bg-white/10'));
1428
1518
  }
1519
+ hrefFor(item) {
1520
+ return navItemHref(this.router, item);
1521
+ }
1429
1522
  onTriggerClick(event) {
1430
1523
  event.preventDefault();
1431
1524
  event.stopPropagation();
@@ -1500,11 +1593,35 @@ class ActionMenuComponent {
1500
1593
  onOverlayDetach() {
1501
1594
  this.open.set(false);
1502
1595
  }
1596
+ /**
1597
+ * Primary click on a routed item: in-app activate via {@link onClick} or
1598
+ * router. Modified clicks keep native link behaviour (new tab).
1599
+ */
1600
+ onRoutedItemClick(event, item) {
1601
+ if (item.disabled) {
1602
+ event.preventDefault();
1603
+ return;
1604
+ }
1605
+ if (isModifiedClick(event)) {
1606
+ this.close();
1607
+ return;
1608
+ }
1609
+ event.preventDefault();
1610
+ this.activateItem(item);
1611
+ }
1503
1612
  selectItem(item) {
1504
1613
  if (item.disabled) {
1505
1614
  return;
1506
1615
  }
1507
- item.onClick();
1616
+ this.activateItem(item);
1617
+ }
1618
+ activateItem(item) {
1619
+ if (item.onClick) {
1620
+ item.onClick();
1621
+ }
1622
+ else if (item.routerLink != null) {
1623
+ void navigateNavItem(this.router, item, false);
1624
+ }
1508
1625
  this.close();
1509
1626
  }
1510
1627
  toggle() {
@@ -1606,20 +1723,41 @@ class ActionMenuComponent {
1606
1723
  role="separator"
1607
1724
  ></div>
1608
1725
  }
1609
- <button
1610
- type="button"
1611
- role="menuitem"
1612
- [id]="itemDomId(i)"
1613
- [class]="itemClass(item, i)"
1614
- [disabled]="!!item.disabled"
1615
- (click)="selectItem(item)"
1616
- (mouseenter)="activeIndex.set(i)"
1617
- >
1618
- @if (item.icon; as icon) {
1619
- <africanies-icon [name]="icon" [size]="16" class="shrink-0" />
1620
- }
1621
- <span class="min-w-0 flex-1 truncate text-left">{{ item.label }}</span>
1622
- </button>
1726
+ @if (item.routerLink !== null && item.routerLink !== undefined) {
1727
+ <a
1728
+ role="menuitem"
1729
+ [id]="itemDomId(i)"
1730
+ [class]="itemClass(item, i)"
1731
+ [attr.href]="item.disabled ? null : hrefFor(item)"
1732
+ [attr.aria-disabled]="item.disabled ? true : null"
1733
+ (click)="onRoutedItemClick($event, item)"
1734
+ (mouseenter)="activeIndex.set(i)"
1735
+ >
1736
+ @if (item.icon; as icon) {
1737
+ <africanies-icon [name]="icon" [size]="16" class="shrink-0" />
1738
+ }
1739
+ <span class="min-w-0 flex-1 truncate text-left">{{
1740
+ item.label
1741
+ }}</span>
1742
+ </a>
1743
+ } @else {
1744
+ <button
1745
+ type="button"
1746
+ role="menuitem"
1747
+ [id]="itemDomId(i)"
1748
+ [class]="itemClass(item, i)"
1749
+ [disabled]="!!item.disabled"
1750
+ (click)="selectItem(item)"
1751
+ (mouseenter)="activeIndex.set(i)"
1752
+ >
1753
+ @if (item.icon; as icon) {
1754
+ <africanies-icon [name]="icon" [size]="16" class="shrink-0" />
1755
+ }
1756
+ <span class="min-w-0 flex-1 truncate text-left">{{
1757
+ item.label
1758
+ }}</span>
1759
+ </button>
1760
+ }
1623
1761
  }
1624
1762
  </div>
1625
1763
  </ng-template>
@@ -1702,20 +1840,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
1702
1840
  role="separator"
1703
1841
  ></div>
1704
1842
  }
1705
- <button
1706
- type="button"
1707
- role="menuitem"
1708
- [id]="itemDomId(i)"
1709
- [class]="itemClass(item, i)"
1710
- [disabled]="!!item.disabled"
1711
- (click)="selectItem(item)"
1712
- (mouseenter)="activeIndex.set(i)"
1713
- >
1714
- @if (item.icon; as icon) {
1715
- <africanies-icon [name]="icon" [size]="16" class="shrink-0" />
1716
- }
1717
- <span class="min-w-0 flex-1 truncate text-left">{{ item.label }}</span>
1718
- </button>
1843
+ @if (item.routerLink !== null && item.routerLink !== undefined) {
1844
+ <a
1845
+ role="menuitem"
1846
+ [id]="itemDomId(i)"
1847
+ [class]="itemClass(item, i)"
1848
+ [attr.href]="item.disabled ? null : hrefFor(item)"
1849
+ [attr.aria-disabled]="item.disabled ? true : null"
1850
+ (click)="onRoutedItemClick($event, item)"
1851
+ (mouseenter)="activeIndex.set(i)"
1852
+ >
1853
+ @if (item.icon; as icon) {
1854
+ <africanies-icon [name]="icon" [size]="16" class="shrink-0" />
1855
+ }
1856
+ <span class="min-w-0 flex-1 truncate text-left">{{
1857
+ item.label
1858
+ }}</span>
1859
+ </a>
1860
+ } @else {
1861
+ <button
1862
+ type="button"
1863
+ role="menuitem"
1864
+ [id]="itemDomId(i)"
1865
+ [class]="itemClass(item, i)"
1866
+ [disabled]="!!item.disabled"
1867
+ (click)="selectItem(item)"
1868
+ (mouseenter)="activeIndex.set(i)"
1869
+ >
1870
+ @if (item.icon; as icon) {
1871
+ <africanies-icon [name]="icon" [size]="16" class="shrink-0" />
1872
+ }
1873
+ <span class="min-w-0 flex-1 truncate text-left">{{
1874
+ item.label
1875
+ }}</span>
1876
+ </button>
1877
+ }
1719
1878
  }
1720
1879
  </div>
1721
1880
  </ng-template>
@@ -1771,7 +1930,7 @@ class EmptyStateComponent {
1771
1930
  <button
1772
1931
  africanies-button
1773
1932
  type="button"
1774
- variant="secondary"
1933
+ variant="flat"
1775
1934
  [disabled]="refreshing()"
1776
1935
  (click)="retry.emit()"
1777
1936
  >
@@ -1802,7 +1961,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
1802
1961
  <button
1803
1962
  africanies-button
1804
1963
  type="button"
1805
- variant="secondary"
1964
+ variant="flat"
1806
1965
  [disabled]="refreshing()"
1807
1966
  (click)="retry.emit()"
1808
1967
  >
@@ -2016,7 +2175,7 @@ class ErrorStateComponent {
2016
2175
  <button
2017
2176
  africanies-button
2018
2177
  type="button"
2019
- variant="secondary"
2178
+ variant="flat"
2020
2179
  [disabled]="refreshing()"
2021
2180
  (click)="retry.emit()"
2022
2181
  >
@@ -2047,7 +2206,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2047
2206
  <button
2048
2207
  africanies-button
2049
2208
  type="button"
2050
- variant="secondary"
2209
+ variant="flat"
2051
2210
  [disabled]="refreshing()"
2052
2211
  (click)="retry.emit()"
2053
2212
  >
@@ -2384,6 +2543,128 @@ function isEmptyData(data) {
2384
2543
  * {@link AsyncStateComponent}) around tables and resource views.
2385
2544
  */
2386
2545
 
2546
+ /**
2547
+ * Bordered expandable panel with animated fold — the pattern used on
2548
+ * Manifest detail and Make Payment review.
2549
+ *
2550
+ * Parent owns open state via {@link open} (`model()` / `[(open)]`).
2551
+ *
2552
+ * @example
2553
+ * ```html
2554
+ * <africanies-accordion
2555
+ * title="Customer Information"
2556
+ * [open]="isOpen('customer')"
2557
+ * (openChange)="onOpenChange('customer', $event)"
2558
+ * >
2559
+ * <dl>…</dl>
2560
+ * </africanies-accordion>
2561
+ * ```
2562
+ */
2563
+ class AccordionComponent {
2564
+ /** Plain-text header. Prefer projected `[africaniesAccordionTitle]` for rich titles. */
2565
+ title = input('', /* @ts-ignore */
2566
+ ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
2567
+ /** Whether the panel body is expanded. */
2568
+ open = model(false, /* @ts-ignore */
2569
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
2570
+ /** Padding / chevron density. */
2571
+ size = input('md', /* @ts-ignore */
2572
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
2573
+ chevronSize = computed(() => this.size() === 'sm' ? 14 : 16, /* @ts-ignore */
2574
+ ...(ngDevMode ? [{ debugName: "chevronSize" }] : /* istanbul ignore next */ []));
2575
+ shellClass = computed(() => this.size() === 'sm'
2576
+ ? 'rounded-lg border border-border dark:border-white/10'
2577
+ : 'rounded-xl border border-border dark:border-white/10', /* @ts-ignore */
2578
+ ...(ngDevMode ? [{ debugName: "shellClass" }] : /* istanbul ignore next */ []));
2579
+ triggerClass = computed(() => this.size() === 'sm'
2580
+ ? 'flex w-full items-center justify-between gap-3 px-3 py-2 text-left text-ink dark:text-white'
2581
+ : 'flex w-full items-center justify-between gap-3 px-4 py-3 text-left text-ink dark:text-white', /* @ts-ignore */
2582
+ ...(ngDevMode ? [{ debugName: "triggerClass" }] : /* istanbul ignore next */ []));
2583
+ bodyClass = computed(() => this.size() === 'sm'
2584
+ ? 'border-t border-border px-3 py-2 dark:border-white/10'
2585
+ : 'border-t border-border px-4 py-3 dark:border-white/10', /* @ts-ignore */
2586
+ ...(ngDevMode ? [{ debugName: "bodyClass" }] : /* istanbul ignore next */ []));
2587
+ toggle() {
2588
+ this.open.update((value) => !value);
2589
+ }
2590
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2591
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AccordionComponent, isStandalone: true, selector: "africanies-accordion", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange" }, host: { classAttribute: "block" }, ngImport: i0, template: `
2592
+ <div [class]="shellClass()">
2593
+ <button
2594
+ type="button"
2595
+ [class]="triggerClass()"
2596
+ [attr.aria-expanded]="open()"
2597
+ (click)="toggle()"
2598
+ >
2599
+ <span class="min-w-0 flex-1 font-medium">
2600
+ <ng-content select="[africaniesAccordionTitle]" />
2601
+ @if (title()) {
2602
+ {{ title() }}
2603
+ }
2604
+ </span>
2605
+ <africanies-icon
2606
+ name="chevron-down"
2607
+ class="shrink-0 transition-transform duration-200"
2608
+ [class.rotate-180]="open()"
2609
+ [size]="chevronSize()"
2610
+ />
2611
+ </button>
2612
+ <div
2613
+ class="africanies-accordion-fold"
2614
+ [class.africanies-accordion-fold-open]="open()"
2615
+ >
2616
+ <div class="africanies-accordion-fold-body">
2617
+ <div [class]="bodyClass()">
2618
+ <ng-content />
2619
+ </div>
2620
+ </div>
2621
+ </div>
2622
+ </div>
2623
+ `, isInline: true, styles: [".africanies-accordion-fold{display:grid;grid-template-rows:0fr;transition:grid-template-rows .2s ease}.africanies-accordion-fold-open{grid-template-rows:1fr}.africanies-accordion-fold-body{min-height:0;overflow:hidden}@media(prefers-reduced-motion:reduce){.africanies-accordion-fold{transition:none}}\n"], dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2624
+ }
2625
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AccordionComponent, decorators: [{
2626
+ type: Component,
2627
+ args: [{ selector: 'africanies-accordion', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AfricaniesIconComponent], host: {
2628
+ class: 'block',
2629
+ }, template: `
2630
+ <div [class]="shellClass()">
2631
+ <button
2632
+ type="button"
2633
+ [class]="triggerClass()"
2634
+ [attr.aria-expanded]="open()"
2635
+ (click)="toggle()"
2636
+ >
2637
+ <span class="min-w-0 flex-1 font-medium">
2638
+ <ng-content select="[africaniesAccordionTitle]" />
2639
+ @if (title()) {
2640
+ {{ title() }}
2641
+ }
2642
+ </span>
2643
+ <africanies-icon
2644
+ name="chevron-down"
2645
+ class="shrink-0 transition-transform duration-200"
2646
+ [class.rotate-180]="open()"
2647
+ [size]="chevronSize()"
2648
+ />
2649
+ </button>
2650
+ <div
2651
+ class="africanies-accordion-fold"
2652
+ [class.africanies-accordion-fold-open]="open()"
2653
+ >
2654
+ <div class="africanies-accordion-fold-body">
2655
+ <div [class]="bodyClass()">
2656
+ <ng-content />
2657
+ </div>
2658
+ </div>
2659
+ </div>
2660
+ </div>
2661
+ `, styles: [".africanies-accordion-fold{display:grid;grid-template-rows:0fr;transition:grid-template-rows .2s ease}.africanies-accordion-fold-open{grid-template-rows:1fr}.africanies-accordion-fold-body{min-height:0;overflow:hidden}@media(prefers-reduced-motion:reduce){.africanies-accordion-fold{transition:none}}\n"] }]
2662
+ }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
2663
+
2664
+ /**
2665
+ * Expandable bordered panels (`africanies-accordion`).
2666
+ */
2667
+
2387
2668
  const DEFAULT_ICONS = {
2388
2669
  info: 'info-circle',
2389
2670
  success: 'check-circle',
@@ -3805,10 +4086,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
3805
4086
  /** Label above the control. Hidden when empty so callers can omit a label. */
3806
4087
  const FORM_LABEL_CLASS = 'block text-body-sm font-medium text-ink dark:text-white mb-1.5 empty:hidden';
3807
4088
  /** Default control shell — `h-10` matches md `africanies-button`. */
3808
- 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 ' +
4089
+ const FORM_FIELD_CLASS = 'flex items-center w-full h-10 box-border rounded-md border border-border bg-white dark:bg-ink-950 text-ink dark:text-white dark:border-white/25 ' +
3809
4090
  'focus-within:outline focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-ink';
3810
4091
  /**
3811
- * Error border — `!` so it wins over shell `border-neutral-300` /
4092
+ * Error border — `!` so it wins over shell `border-border` /
3812
4093
  * `dark:border-white/25` (same specificity; those utilities sort later in the
3813
4094
  * generated CSS and otherwise paint over `border-danger`).
3814
4095
  *
@@ -4595,7 +4876,7 @@ class ModalService {
4595
4876
  'africanies-overlay-backdrop',
4596
4877
  'bg-ink/45',
4597
4878
  'backdrop-blur-sm',
4598
- 'dark:bg-ink/60',
4879
+ 'dark:bg-ink-950/60',
4599
4880
  ],
4600
4881
  panelClass: [
4601
4882
  'africanies-modal-panel',
@@ -5409,7 +5690,7 @@ class SelectComponent {
5409
5690
  @if (multiple() && selectedList().length) {
5410
5691
  @for (chip of selectedList(); track trackOption(chip)) {
5411
5692
  <span
5412
- 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"
5693
+ 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"
5413
5694
  >
5414
5695
  @if (chip.prefix; as prefixIcon) {
5415
5696
  <africanies-icon [name]="prefixIcon" [size]="14" class="shrink-0" />
@@ -5737,7 +6018,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
5737
6018
  @if (multiple() && selectedList().length) {
5738
6019
  @for (chip of selectedList(); track trackOption(chip)) {
5739
6020
  <span
5740
- 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"
6021
+ 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"
5741
6022
  >
5742
6023
  @if (chip.prefix; as prefixIcon) {
5743
6024
  <africanies-icon [name]="prefixIcon" [size]="14" class="shrink-0" />
@@ -7332,7 +7613,7 @@ class DrawerService {
7332
7613
  'africanies-overlay-backdrop',
7333
7614
  'bg-ink/45',
7334
7615
  'backdrop-blur-sm',
7335
- 'dark:bg-ink/60',
7616
+ 'dark:bg-ink-950/60',
7336
7617
  ],
7337
7618
  panelClass: [
7338
7619
  'africanies-drawer-panel',
@@ -9444,7 +9725,7 @@ class FilePreviewDialogComponent {
9444
9725
  <img
9445
9726
  [src]="src()"
9446
9727
  [alt]="data.file.name"
9447
- 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"
9728
+ 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"
9448
9729
  />
9449
9730
  } @else {
9450
9731
  <div
@@ -9514,7 +9795,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
9514
9795
  <img
9515
9796
  [src]="src()"
9516
9797
  [alt]="data.file.name"
9517
- 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"
9798
+ 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"
9518
9799
  />
9519
9800
  } @else {
9520
9801
  <div
@@ -9997,7 +10278,7 @@ class FileUploadComponent {
9997
10278
  <button
9998
10279
  africanies-button
9999
10280
  type="button"
10000
- variant="secondary"
10281
+ variant="flat"
10001
10282
  size="sm"
10002
10283
  [disabled]="disabled()"
10003
10284
  (click)="openFilePicker()"
@@ -10010,7 +10291,7 @@ class FileUploadComponent {
10010
10291
  <button
10011
10292
  africanies-button
10012
10293
  type="button"
10013
- variant="secondary"
10294
+ variant="flat"
10014
10295
  size="sm"
10015
10296
  [disabled]="disabled()"
10016
10297
  (click)="openCamera()"
@@ -10065,7 +10346,7 @@ class FileUploadComponent {
10065
10346
  <button
10066
10347
  africanies-button
10067
10348
  type="button"
10068
- variant="secondary"
10349
+ variant="flat"
10069
10350
  size="sm"
10070
10351
  [disabled]="disabled()"
10071
10352
  (click)="openFilePicker()"
@@ -10134,7 +10415,7 @@ class FileUploadComponent {
10134
10415
  <button
10135
10416
  africanies-button
10136
10417
  type="button"
10137
- variant="secondary"
10418
+ variant="flat"
10138
10419
  size="sm"
10139
10420
  [disabled]="disabled()"
10140
10421
  (click)="openFilePicker(); $event.stopPropagation()"
@@ -10297,7 +10578,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
10297
10578
  <button
10298
10579
  africanies-button
10299
10580
  type="button"
10300
- variant="secondary"
10581
+ variant="flat"
10301
10582
  size="sm"
10302
10583
  [disabled]="disabled()"
10303
10584
  (click)="openFilePicker()"
@@ -10310,7 +10591,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
10310
10591
  <button
10311
10592
  africanies-button
10312
10593
  type="button"
10313
- variant="secondary"
10594
+ variant="flat"
10314
10595
  size="sm"
10315
10596
  [disabled]="disabled()"
10316
10597
  (click)="openCamera()"
@@ -10365,7 +10646,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
10365
10646
  <button
10366
10647
  africanies-button
10367
10648
  type="button"
10368
- variant="secondary"
10649
+ variant="flat"
10369
10650
  size="sm"
10370
10651
  [disabled]="disabled()"
10371
10652
  (click)="openFilePicker()"
@@ -10434,7 +10715,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
10434
10715
  <button
10435
10716
  africanies-button
10436
10717
  type="button"
10437
- variant="secondary"
10718
+ variant="flat"
10438
10719
  size="sm"
10439
10720
  [disabled]="disabled()"
10440
10721
  (click)="openFilePicker(); $event.stopPropagation()"
@@ -12925,84 +13206,6 @@ function finalizeLeaf(crumbs, path) {
12925
13206
  return crumbs;
12926
13207
  }
12927
13208
 
12928
- /**
12929
- * Builds a {@link UrlTree} for an item’s `routerLink` / query / fragment.
12930
- *
12931
- * @param router - Consumer app router.
12932
- * @param item - Nav item with a `routerLink`.
12933
- * @returns UrlTree, or `null` when the item is not routed.
12934
- */
12935
- function navItemUrlTree(router, item) {
12936
- if (item.routerLink == null) {
12937
- return null;
12938
- }
12939
- const commands = Array.isArray(item.routerLink)
12940
- ? [...item.routerLink]
12941
- : [item.routerLink];
12942
- return router.createUrlTree(commands, {
12943
- queryParams: item.queryParams,
12944
- fragment: item.fragment,
12945
- });
12946
- }
12947
- /**
12948
- * Whether the current router URL matches this item (cold load + navigations).
12949
- *
12950
- * @param router - Consumer app router.
12951
- * @param item - Nav item with a `routerLink`.
12952
- * @param options - Same shape as `RouterLinkActive` options.
12953
- * @returns True when the current URL matches the item.
12954
- */
12955
- function isNavItemActive(router, item, options) {
12956
- const tree = navItemUrlTree(router, item);
12957
- if (!tree) {
12958
- return false;
12959
- }
12960
- return router.isActive(tree, options);
12961
- }
12962
-
12963
- /**
12964
- * Whether a click should leave navigation to the browser (new tab, etc.).
12965
- * @param event - Mouse click from a routed nav control.
12966
- * @returns True when the host should not call `preventDefault` / navigate.
12967
- */
12968
- function isModifiedClick(event) {
12969
- return (event.button !== 0 ||
12970
- event.metaKey ||
12971
- event.ctrlKey ||
12972
- event.shiftKey ||
12973
- event.altKey);
12974
- }
12975
- /**
12976
- * Serialized href for a routed nav item (for `<a href>` without RouterLink).
12977
- * @param router - App router.
12978
- * @param item - Nav item with `routerLink`.
12979
- * @returns Path string, or `null` when the item is not routed.
12980
- */
12981
- function navItemHref(router, item) {
12982
- const tree = navItemUrlTree(router, item);
12983
- return tree ? router.serializeUrl(tree) : null;
12984
- }
12985
- /**
12986
- * Navigate to a nav item without jumping the viewport to the top.
12987
- *
12988
- * Apps that enable `withInMemoryScrolling({ scrollPositionRestoration:
12989
- * 'enabled' })` scroll to `[0, 0]` on every forward navigation. That is wrong
12990
- * for in-page tabs/segments. Passing `{ scroll: 'manual' }` tells Angular's
12991
- * RouterScroller to skip scrolling for this navigation.
12992
- *
12993
- * @param router - App router.
12994
- * @param item - Nav item with `routerLink`.
12995
- * @param keepScroll - When true (default for tabs/segments), skip router scroll.
12996
- * @returns Navigation promise, or `null` when the item is not routed.
12997
- */
12998
- function navigateNavItem(router, item, keepScroll = true) {
12999
- const tree = navItemUrlTree(router, item);
13000
- if (!tree) {
13001
- return null;
13002
- }
13003
- return router.navigateByUrl(tree, keepScroll ? { scroll: 'manual' } : undefined);
13004
- }
13005
-
13006
13209
  const DEFAULT_LINK_ACTIVE$2 = {
13007
13210
  paths: 'exact',
13008
13211
  queryParams: 'exact',
@@ -13273,8 +13476,8 @@ class ShippingModeSwitchComponent {
13273
13476
  mode = this.shipping.mode;
13274
13477
  /** Host padding / divider — tighter when the rail is collapsed. */
13275
13478
  hostClass = computed(() => this.collapsed()
13276
- ? 'block shrink-0 border-t border-border px-1.5 py-3 dark:border-white/10'
13277
- : 'block shrink-0 border-t border-border px-2 py-3 dark:border-white/10', /* @ts-ignore */
13479
+ ? 'block shrink-0 border-t border-strong px-1.5 py-3 dark:border-white/10'
13480
+ : 'block shrink-0 border-t border-strong px-2 py-3 dark:border-white/10', /* @ts-ignore */
13278
13481
  ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
13279
13482
  /**
13280
13483
  * Requests a mode change through {@link ShippingModeService.requestModeChange}
@@ -13586,9 +13789,10 @@ const DEFAULT_LINK_ACTIVE$1 = {
13586
13789
  /**
13587
13790
  * App shell side navigation with icons, nested items, and a collapsible rail.
13588
13791
  *
13589
- * **Design - ink spine:** a continuous mode-accent edge with soft active
13590
- * highlights. Collapsed mode keeps icons; hover opens a floating label blade
13591
- * (name + children) instead of a plain tooltip.
13792
+ * **Design - ink spine:** a continuous mode-accent edge; active leaves use
13793
+ * primary fill + white text (portal parity); expanded parents of an active
13794
+ * child use primary text on white. Collapsed mode keeps icons; hover opens a
13795
+ * floating label blade (name + children) instead of a plain tooltip.
13592
13796
  *
13593
13797
  * ## Parent items with children
13594
13798
  * **Expanded rail:** branches start open by default (`expandBranchesByDefault`).
@@ -13743,11 +13947,14 @@ class SideNavComponent {
13743
13947
  (item.disabled ? ' cursor-not-allowed opacity-50' : ' cursor-pointer');
13744
13948
  const leaf = this.isLeafActive(item);
13745
13949
  const ancestor = !leaf && this.isItemActive(item);
13950
+ const colors = this.modeColor.classes();
13951
+ // Portal: active leaf / collapsed rail = primary fill + white text.
13746
13952
  if (leaf) {
13747
- return `${base} ${this.modeColor.classes().soft} ${this.modeColor.classes().text} font-semibold`;
13953
+ return `${base} ${colors.activeFill} font-semibold`;
13748
13954
  }
13955
+ // Portal: expanded parent of active child = primary text on white.
13749
13956
  if (ancestor) {
13750
- return `${base} ${this.modeColor.classes().soft} ${this.modeColor.classes().text} font-semibold`;
13957
+ return `${base} bg-transparent ${colors.text} font-semibold`;
13751
13958
  }
13752
13959
  return (`${base} font-medium text-neutral-600 hover:bg-background-welcome hover:text-ink ` +
13753
13960
  'dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white');
@@ -13757,7 +13964,7 @@ class SideNavComponent {
13757
13964
  'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink ' +
13758
13965
  (item.disabled ? 'cursor-not-allowed opacity-50 ' : 'cursor-pointer ');
13759
13966
  if (this.isLeafActive(item)) {
13760
- return `${base} ${this.modeColor.classes().soft} ${this.modeColor.classes().text} font-semibold`;
13967
+ return `${base} ${this.modeColor.classes().activeFill} font-semibold`;
13761
13968
  }
13762
13969
  return (`${base} font-medium text-neutral-600 hover:bg-background-welcome hover:text-ink ` +
13763
13970
  'dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white');
@@ -13902,13 +14109,13 @@ class SideNavComponent {
13902
14109
  return null;
13903
14110
  }
13904
14111
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SideNavComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13905
- 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 });
14112
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: SideNavComponent, isStandalone: true, selector: "africanies-side-nav", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, activeId: { classPropertyName: "activeId", publicName: "activeId", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, expandParents: { classPropertyName: "expandParents", publicName: "expandParents", isSignal: true, isRequired: false, transformFunction: null }, expandBranchesByDefault: { classPropertyName: "expandBranchesByDefault", publicName: "expandBranchesByDefault", isSignal: true, isRequired: false, transformFunction: null }, linkActiveOptions: { classPropertyName: "linkActiveOptions", publicName: "linkActiveOptions", isSignal: true, isRequired: false, transformFunction: null }, showLogo: { classPropertyName: "showLogo", publicName: "showLogo", isSignal: true, isRequired: false, transformFunction: null }, logoSrc: { classPropertyName: "logoSrc", publicName: "logoSrc", isSignal: true, isRequired: false, transformFunction: null }, logoMiniSrc: { classPropertyName: "logoMiniSrc", publicName: "logoMiniSrc", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsed: "collapsedChange", activeId: "activeIdChange" }, host: { classAttribute: "relative z-20 block h-full" }, ngImport: i0, template: " <aside\n class=\"relative flex h-full flex-col overflow-visible border-r border-border bg-white transition-[width] duration-200 ease-out dark:border-white/10 dark:bg-ink-950\"\n [style.width]=\"collapsed() ? '3.75rem' : '15rem'\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n <div\n class=\"pointer-events-none absolute inset-y-0 left-0 w-[3px] transition-colors\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n\n <div\n class=\"flex shrink-0 items-center gap-2 border-b border-border py-3 dark:border-white/10\"\n [class.flex-col]=\"collapsed()\"\n [class.px-3]=\"collapsed()\"\n [class.pl-4]=\"!collapsed()\"\n [class.pr-2]=\"!collapsed()\"\n >\n @if (collapsed()) {\n @if (showLogo()) {\n <img\n [src]=\"resolvedMiniLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"size-8 shrink-0 object-contain\"\n />\n }\n } @else {\n @if (showLogo()) {\n <div class=\"min-w-0 flex-1\">\n <img\n [src]=\"resolvedFullLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"h-9 max-w-full object-contain object-left\"\n />\n </div>\n } @else {\n <div\n class=\"min-w-0 flex-1 truncate text-body-sm font-semibold text-ink dark:text-white\"\n >\n <ng-content select=\"[brand]\" />\n </div>\n }\n }\n <button\n type=\"button\"\n class=\"inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white\"\n [attr.aria-label]=\"\n collapsed() ? 'Expand navigation' : 'Collapse navigation'\n \"\n [attr.aria-expanded]=\"!collapsed()\"\n (click)=\"toggleCollapsed()\"\n >\n <africanies-icon\n [name]=\"collapsed() ? 'angle-double-right' : 'angle-double-left'\"\n [size]=\"16\"\n />\n </button>\n </div>\n\n <nav class=\"africanies-overlay-scroll relative flex-1 overflow-y-auto overflow-x-visible px-3 py-3\">\n @if (!collapsed() && expandParents() && hasOpenBranches()) {\n <button\n type=\"button\"\n class=\"absolute right-1.5 top-1.5 z-10 inline-flex size-7 cursor-pointer items-center justify-center rounded-md bg-white text-neutral-500 shadow-sm ring-1 ring-border/80 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:bg-ink-950 dark:text-neutral-400 dark:ring-white/15 dark:hover:bg-white/10 dark:hover:text-white\"\n aria-label=\"Collapse all\"\n (click)=\"collapseAllBranches()\"\n >\n <africanies-icon name=\"compress\" [size]=\"14\" />\n </button>\n }\n <ul class=\"m-0 flex list-none flex-col gap-0.5 p-0\">\n @for (item of items(); track item.id) {\n <li\n class=\"relative overflow-visible\"\n (mouseenter)=\"onItemEnter(item.id)\"\n (mouseleave)=\"onItemLeave(item.id)\"\n >\n @if (item.routerLink !== null && item.routerLink !== undefined) {\n <a\n [routerLink]=\"item.disabled ? null : item.routerLink\"\n [queryParams]=\"item.queryParams\"\n [fragment]=\"item.fragment\"\n [class]=\"rowClass(item)\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n [attr.aria-disabled]=\"item.disabled ? true : null\"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"rowClass(item)\"\n [disabled]=\"!!item.disabled\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </button>\n }\n\n @if (!collapsed() && item.children?.length) {\n <div\n class=\"grid transition-[grid-template-rows] duration-200 ease-out\"\n [style.grid-template-rows]=\"\n isBranchOpen(item.id) ? '1fr' : '0fr'\n \"\n [attr.aria-hidden]=\"!isBranchOpen(item.id)\"\n [class.pointer-events-none]=\"!isBranchOpen(item.id)\"\n >\n <ul\n class=\"relative m-0 mt-0.5 min-h-0 list-none space-y-0.5 overflow-hidden py-1 pl-4 before:absolute before:bottom-1 before:left-[1.35rem] before:top-0 before:w-px before:bg-border dark:before:bg-white/15\"\n >\n @for (child of item.children; track child.id) {\n <li\n class=\"transition-opacity duration-200 ease-out\"\n [class.opacity-0]=\"!isBranchOpen(item.id)\"\n [class.opacity-100]=\"isBranchOpen(item.id)\"\n >\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"child.disabled ? null : child.routerLink\"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n [class]=\"childRowClass(child)\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"childRowClass(child)\"\n [disabled]=\"!!child.disabled\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n </div>\n }\n\n @if (collapsed() && hoverId() === item.id) {\n <div\n class=\"absolute left-full top-0 z-50 ml-2 min-w-[11rem] overflow-hidden rounded-md border border-border bg-ink text-white shadow-lg dark:border-white/15\"\n role=\"tooltip\"\n >\n <div\n class=\"absolute inset-y-0 left-0 w-1\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n <div class=\"border-b border-white/10 px-3 py-2 pl-4\">\n <p class=\"m-0 text-body-sm font-semibold\">{{ item.label }}</p>\n </div>\n @if (item.children?.length) {\n <ul class=\"m-0 list-none space-y-0.5 p-1.5\">\n @for (child of item.children; track child.id) {\n <li>\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"\n child.disabled ? null : child.routerLink\n \"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n class=\"relative flex items-center gap-2 rounded px-2.5 py-1.5 text-body-sm text-white/85 no-underline transition-colors hover:bg-white/10 hover:text-white\"\n [class.opacity-50]=\"child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"relative flex w-full items-center gap-2 rounded px-2.5 py-1.5 text-left text-body-sm text-white/85 transition-colors hover:bg-white/10 hover:text-white\"\n [disabled]=\"!!child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n }\n </div>\n }\n </li>\n }\n </ul>\n </nav>\n\n <ng-content select=\"[footer]\" />\n </aside>\n\n <ng-template #rowInner let-item>\n <span class=\"inline-flex size-5 shrink-0 items-center justify-center\">\n @if (item.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"16\" />\n } @else {\n <span\n class=\"size-1.5 rounded-full bg-current opacity-70\"\n aria-hidden=\"true\"\n ></span>\n }\n </span>\n @if (!collapsed()) {\n <span class=\"min-w-0 flex-1 truncate text-left\">{{ item.label }}</span>\n @if (item.children?.length) {\n <africanies-icon\n name=\"chevron-down\"\n [size]=\"14\"\n class=\"shrink-0 opacity-70 transition-transform duration-200 ease-out\"\n [class.rotate-180]=\"isBranchOpen(item.id)\"\n />\n }\n }\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: item.badge,\n compact: collapsed(),\n }\"\n />\n </ng-template>\n\n <ng-template #navBadge let-visible=\"visible\" let-compact=\"compact\">\n @if (visible) {\n <span\n class=\"pointer-events-none absolute z-10 overflow-visible\"\n [class]=\"compact ? 'right-0.5 top-0.5 h-2 w-2' : 'right-1.5 top-1 h-2 w-2'\"\n aria-hidden=\"true\"\n >\n <span\n class=\"africanies-side-nav-badge-ping absolute left-1/2 top-1/2 size-2 rounded-full bg-danger\"\n ></span>\n <span\n class=\"absolute left-1/2 top-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-danger ring-2 ring-white dark:ring-ink\"\n ></span>\n </span>\n }\n </ng-template>", styles: ["@keyframes africanies-side-nav-badge-pulse{0%{transform:translate(-50%,-50%) scale(.85);opacity:.9}70%{transform:translate(-50%,-50%) scale(2.4);opacity:0}to{transform:translate(-50%,-50%) scale(2.4);opacity:0}}.africanies-side-nav-badge-ping{animation:africanies-side-nav-badge-pulse 1.4s cubic-bezier(0,0,.2,1) infinite}@media(prefers-reduced-motion:reduce){.africanies-side-nav-badge-ping{animation:none;opacity:.5}}\n"], dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13906
14113
  }
13907
14114
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SideNavComponent, decorators: [{
13908
14115
  type: Component,
13909
14116
  args: [{ selector: 'africanies-side-nav', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AfricaniesIconComponent, RouterLink, NgTemplateOutlet], host: {
13910
14117
  class: 'relative z-20 block h-full',
13911
- }, 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"] }]
14118
+ }, template: " <aside\n class=\"relative flex h-full flex-col overflow-visible border-r border-border bg-white transition-[width] duration-200 ease-out dark:border-white/10 dark:bg-ink-950\"\n [style.width]=\"collapsed() ? '3.75rem' : '15rem'\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n <div\n class=\"pointer-events-none absolute inset-y-0 left-0 w-[3px] transition-colors\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n\n <div\n class=\"flex shrink-0 items-center gap-2 border-b border-border py-3 dark:border-white/10\"\n [class.flex-col]=\"collapsed()\"\n [class.px-3]=\"collapsed()\"\n [class.pl-4]=\"!collapsed()\"\n [class.pr-2]=\"!collapsed()\"\n >\n @if (collapsed()) {\n @if (showLogo()) {\n <img\n [src]=\"resolvedMiniLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"size-8 shrink-0 object-contain\"\n />\n }\n } @else {\n @if (showLogo()) {\n <div class=\"min-w-0 flex-1\">\n <img\n [src]=\"resolvedFullLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"h-9 max-w-full object-contain object-left\"\n />\n </div>\n } @else {\n <div\n class=\"min-w-0 flex-1 truncate text-body-sm font-semibold text-ink dark:text-white\"\n >\n <ng-content select=\"[brand]\" />\n </div>\n }\n }\n <button\n type=\"button\"\n class=\"inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white\"\n [attr.aria-label]=\"\n collapsed() ? 'Expand navigation' : 'Collapse navigation'\n \"\n [attr.aria-expanded]=\"!collapsed()\"\n (click)=\"toggleCollapsed()\"\n >\n <africanies-icon\n [name]=\"collapsed() ? 'angle-double-right' : 'angle-double-left'\"\n [size]=\"16\"\n />\n </button>\n </div>\n\n <nav class=\"africanies-overlay-scroll relative flex-1 overflow-y-auto overflow-x-visible px-3 py-3\">\n @if (!collapsed() && expandParents() && hasOpenBranches()) {\n <button\n type=\"button\"\n class=\"absolute right-1.5 top-1.5 z-10 inline-flex size-7 cursor-pointer items-center justify-center rounded-md bg-white text-neutral-500 shadow-sm ring-1 ring-border/80 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:bg-ink-950 dark:text-neutral-400 dark:ring-white/15 dark:hover:bg-white/10 dark:hover:text-white\"\n aria-label=\"Collapse all\"\n (click)=\"collapseAllBranches()\"\n >\n <africanies-icon name=\"compress\" [size]=\"14\" />\n </button>\n }\n <ul class=\"m-0 flex list-none flex-col gap-0.5 p-0\">\n @for (item of items(); track item.id) {\n <li\n class=\"relative overflow-visible\"\n (mouseenter)=\"onItemEnter(item.id)\"\n (mouseleave)=\"onItemLeave(item.id)\"\n >\n @if (item.routerLink !== null && item.routerLink !== undefined) {\n <a\n [routerLink]=\"item.disabled ? null : item.routerLink\"\n [queryParams]=\"item.queryParams\"\n [fragment]=\"item.fragment\"\n [class]=\"rowClass(item)\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n [attr.aria-disabled]=\"item.disabled ? true : null\"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"rowClass(item)\"\n [disabled]=\"!!item.disabled\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </button>\n }\n\n @if (!collapsed() && item.children?.length) {\n <div\n class=\"grid transition-[grid-template-rows] duration-200 ease-out\"\n [style.grid-template-rows]=\"\n isBranchOpen(item.id) ? '1fr' : '0fr'\n \"\n [attr.aria-hidden]=\"!isBranchOpen(item.id)\"\n [class.pointer-events-none]=\"!isBranchOpen(item.id)\"\n >\n <ul\n class=\"relative m-0 mt-0.5 min-h-0 list-none space-y-0.5 overflow-hidden py-1 pl-4 before:absolute before:bottom-1 before:left-[1.35rem] before:top-0 before:w-px before:bg-border dark:before:bg-white/15\"\n >\n @for (child of item.children; track child.id) {\n <li\n class=\"transition-opacity duration-200 ease-out\"\n [class.opacity-0]=\"!isBranchOpen(item.id)\"\n [class.opacity-100]=\"isBranchOpen(item.id)\"\n >\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"child.disabled ? null : child.routerLink\"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n [class]=\"childRowClass(child)\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"childRowClass(child)\"\n [disabled]=\"!!child.disabled\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n </div>\n }\n\n @if (collapsed() && hoverId() === item.id) {\n <div\n class=\"absolute left-full top-0 z-50 ml-2 min-w-[11rem] overflow-hidden rounded-md border border-border bg-ink text-white shadow-lg dark:border-white/15\"\n role=\"tooltip\"\n >\n <div\n class=\"absolute inset-y-0 left-0 w-1\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n <div class=\"border-b border-white/10 px-3 py-2 pl-4\">\n <p class=\"m-0 text-body-sm font-semibold\">{{ item.label }}</p>\n </div>\n @if (item.children?.length) {\n <ul class=\"m-0 list-none space-y-0.5 p-1.5\">\n @for (child of item.children; track child.id) {\n <li>\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"\n child.disabled ? null : child.routerLink\n \"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n class=\"relative flex items-center gap-2 rounded px-2.5 py-1.5 text-body-sm text-white/85 no-underline transition-colors hover:bg-white/10 hover:text-white\"\n [class.opacity-50]=\"child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"relative flex w-full items-center gap-2 rounded px-2.5 py-1.5 text-left text-body-sm text-white/85 transition-colors hover:bg-white/10 hover:text-white\"\n [disabled]=\"!!child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n }\n </div>\n }\n </li>\n }\n </ul>\n </nav>\n\n <ng-content select=\"[footer]\" />\n </aside>\n\n <ng-template #rowInner let-item>\n <span class=\"inline-flex size-5 shrink-0 items-center justify-center\">\n @if (item.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"16\" />\n } @else {\n <span\n class=\"size-1.5 rounded-full bg-current opacity-70\"\n aria-hidden=\"true\"\n ></span>\n }\n </span>\n @if (!collapsed()) {\n <span class=\"min-w-0 flex-1 truncate text-left\">{{ item.label }}</span>\n @if (item.children?.length) {\n <africanies-icon\n name=\"chevron-down\"\n [size]=\"14\"\n class=\"shrink-0 opacity-70 transition-transform duration-200 ease-out\"\n [class.rotate-180]=\"isBranchOpen(item.id)\"\n />\n }\n }\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: item.badge,\n compact: collapsed(),\n }\"\n />\n </ng-template>\n\n <ng-template #navBadge let-visible=\"visible\" let-compact=\"compact\">\n @if (visible) {\n <span\n class=\"pointer-events-none absolute z-10 overflow-visible\"\n [class]=\"compact ? 'right-0.5 top-0.5 h-2 w-2' : 'right-1.5 top-1 h-2 w-2'\"\n aria-hidden=\"true\"\n >\n <span\n class=\"africanies-side-nav-badge-ping absolute left-1/2 top-1/2 size-2 rounded-full bg-danger\"\n ></span>\n <span\n class=\"absolute left-1/2 top-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-danger ring-2 ring-white dark:ring-ink\"\n ></span>\n </span>\n }\n </ng-template>", styles: ["@keyframes africanies-side-nav-badge-pulse{0%{transform:translate(-50%,-50%) scale(.85);opacity:.9}70%{transform:translate(-50%,-50%) scale(2.4);opacity:0}to{transform:translate(-50%,-50%) scale(2.4);opacity:0}}.africanies-side-nav-badge-ping{animation:africanies-side-nav-badge-pulse 1.4s cubic-bezier(0,0,.2,1) infinite}@media(prefers-reduced-motion:reduce){.africanies-side-nav-badge-ping{animation:none;opacity:.5}}\n"] }]
13912
14119
  }], 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 }] }] } });
13913
14120
 
13914
14121
  /**
@@ -14741,7 +14948,8 @@ class NotificationDrawerPanel {
14741
14948
  ...(ngDevMode ? [{ debugName: "viewLinkClass" }] : /* istanbul ignore next */ []));
14742
14949
  title = computed(() => this.data.title ?? 'Notifications', /* @ts-ignore */
14743
14950
  ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
14744
- items = signal(this.data.onLoadPage ? [] : (this.data.notifications ?? []), /* @ts-ignore */
14951
+ seededItems = this.data.notifications ?? [];
14952
+ items = signal(this.seededItems, /* @ts-ignore */
14745
14953
  ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
14746
14954
  markingId = signal(null, /* @ts-ignore */
14747
14955
  ...(ngDevMode ? [{ debugName: "markingId" }] : /* istanbul ignore next */ []));
@@ -14749,7 +14957,7 @@ class NotificationDrawerPanel {
14749
14957
  ...(ngDevMode ? [{ debugName: "markingAll" }] : /* istanbul ignore next */ []));
14750
14958
  confirmingMarkAll = signal(false, /* @ts-ignore */
14751
14959
  ...(ngDevMode ? [{ debugName: "confirmingMarkAll" }] : /* istanbul ignore next */ []));
14752
- loadingInitial = signal(!!this.data.onLoadPage, /* @ts-ignore */
14960
+ loadingInitial = signal(!!this.data.onLoadPage && this.seededItems.length === 0, /* @ts-ignore */
14753
14961
  ...(ngDevMode ? [{ debugName: "loadingInitial" }] : /* istanbul ignore next */ []));
14754
14962
  loadingMore = signal(false, /* @ts-ignore */
14755
14963
  ...(ngDevMode ? [{ debugName: "loadingMore" }] : /* istanbul ignore next */ []));
@@ -14783,7 +14991,13 @@ class NotificationDrawerPanel {
14783
14991
  markAllConfirmTimer = null;
14784
14992
  constructor() {
14785
14993
  if (this.data.onLoadPage) {
14786
- this.fetchPage(1, 'replace');
14994
+ if (this.seededItems.length > 0) {
14995
+ this.currentPage.set(1);
14996
+ this.fetchPage(1, 'refresh');
14997
+ }
14998
+ else {
14999
+ this.fetchPage(1, 'replace');
15000
+ }
14787
15001
  }
14788
15002
  effect(() => {
14789
15003
  if (this.loadingInitial() ||
@@ -14977,11 +15191,14 @@ class NotificationDrawerPanel {
14977
15191
  this.loadingInitial.set(true);
14978
15192
  this.loadError.set(null);
14979
15193
  }
15194
+ else if (mode === 'refresh') {
15195
+ this.loadError.set(null);
15196
+ }
14980
15197
  else {
14981
15198
  this.loadingMore.set(true);
14982
15199
  }
14983
15200
  this.runPageLoad(() => loadPage(page), (result) => {
14984
- if (mode === 'replace') {
15201
+ if (mode === 'replace' || mode === 'refresh') {
14985
15202
  this.items.set(result.items ?? []);
14986
15203
  }
14987
15204
  else {
@@ -14996,6 +15213,9 @@ class NotificationDrawerPanel {
14996
15213
  this.loadError.set('Could not load notifications. Try again.');
14997
15214
  this.loadingInitial.set(false);
14998
15215
  }
15216
+ else if (mode === 'refresh' && this.items().length === 0) {
15217
+ this.loadError.set('Could not load notifications. Try again.');
15218
+ }
14999
15219
  this.loadingMore.set(false);
15000
15220
  });
15001
15221
  }
@@ -15130,7 +15350,7 @@ class NotificationDrawerPanel {
15130
15350
  <button
15131
15351
  africanies-button
15132
15352
  type="button"
15133
- variant="secondary"
15353
+ variant="flat"
15134
15354
  size="sm"
15135
15355
  (click)="retryInitialLoad()"
15136
15356
  >
@@ -15361,7 +15581,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
15361
15581
  <button
15362
15582
  africanies-button
15363
15583
  type="button"
15364
- variant="secondary"
15584
+ variant="flat"
15365
15585
  size="sm"
15366
15586
  (click)="retryInitialLoad()"
15367
15587
  >
@@ -15795,9 +16015,14 @@ function hashString(value) {
15795
16015
  return hash;
15796
16016
  }
15797
16017
 
15798
- const CACHE_KEY = 'africanies-header-weather-v3';
16018
+ /** Bump when cache shape or city resolution strategy changes. */
16019
+ const CACHE_KEY = 'africanies-header-weather-v4';
15799
16020
  const FETCH_MS = 4_000;
16021
+ const BROWSER_GEO_MS = 3_500;
16022
+ /** IP fallback — city string is often wrong (e.g. NG IPs labelled Lagos). */
15800
16023
  const GEO_URL = 'https://get.geojs.io/v1/ip/geo.json';
16024
+ /** Client reverse-geocode so the label matches the forecast coordinates. */
16025
+ const REVERSE_GEO_URL = 'https://api.bigdatacloud.net/data/reverse-geocode-client';
15801
16026
  const WEATHER_LABELS = {
15802
16027
  clear: 'Clear',
15803
16028
  cloudy: 'Cloudy',
@@ -15863,7 +16088,12 @@ function headerWeatherIcon(kind, hour) {
15863
16088
  return 'cloud-o';
15864
16089
  }
15865
16090
  /**
15866
- * City-level forecast via IP geolocation + Open-Meteo (no API key).
16091
+ * City-level forecast via browser / IP geolocation + Open-Meteo (no API key).
16092
+ *
16093
+ * Coordinates prefer the browser Geolocation API (accurate for travellers;
16094
+ * may prompt once). Otherwise IP geo is used. The city label is reverse-
16095
+ * geocoded from those coordinates — IP `city` strings are often wrong for
16096
+ * Nigeria (many networks resolve as Lagos).
15867
16097
  *
15868
16098
  * Fails closed: missing browser APIs, timeouts, and HTTP errors all return
15869
16099
  * `null` so the greeting can stay time-of-day only. Cached per local hour
@@ -15883,14 +16113,12 @@ async function loadHeaderWeather(fetchFn) {
15883
16113
  return cached;
15884
16114
  }
15885
16115
  try {
15886
- const geo = (await fetchJson(run, GEO_URL));
15887
- const latitude = asNumber(geo?.latitude);
15888
- const longitude = asNumber(geo?.longitude);
15889
- if (latitude === null || longitude === null) {
16116
+ const coords = await resolveCoordinates(run);
16117
+ if (!coords) {
15890
16118
  return null;
15891
16119
  }
15892
16120
  const forecastUrl = `https://api.open-meteo.com/v1/forecast` +
15893
- `?latitude=${latitude}&longitude=${longitude}` +
16121
+ `?latitude=${coords.latitude}&longitude=${coords.longitude}` +
15894
16122
  `&current=weather_code,temperature_2m`;
15895
16123
  const forecast = (await fetchJson(run, forecastUrl));
15896
16124
  const code = asNumber(forecast?.current?.weather_code);
@@ -15902,7 +16130,8 @@ async function loadHeaderWeather(fetchFn) {
15902
16130
  return null;
15903
16131
  }
15904
16132
  const temperatureC = asNumber(forecast?.current?.temperature_2m) ?? undefined;
15905
- const city = asCity(geo?.city);
16133
+ const city = (await reverseGeocodeCity(run, coords.latitude, coords.longitude)) ??
16134
+ coords.fallbackCity;
15906
16135
  const weather = { kind, temperatureC, city };
15907
16136
  writeCache({ hour, kind, temperatureC, city });
15908
16137
  return weather;
@@ -15945,6 +16174,60 @@ function writeCache(value) {
15945
16174
  // Private mode / quota — greeting still works without cache.
15946
16175
  }
15947
16176
  }
16177
+ /**
16178
+ * Prefer device coordinates when available (may prompt once); otherwise fall
16179
+ * back to IP geo. IP city labels for Nigeria are often wrong — reverse
16180
+ * geocoding from these coordinates still depends on accurate lat/lon.
16181
+ */
16182
+ async function resolveCoordinates(fetchFn) {
16183
+ const browser = await tryBrowserGeolocation();
16184
+ if (browser) {
16185
+ return browser;
16186
+ }
16187
+ const geo = (await fetchJson(fetchFn, GEO_URL));
16188
+ const latitude = asNumber(geo?.latitude);
16189
+ const longitude = asNumber(geo?.longitude);
16190
+ if (latitude === null || longitude === null) {
16191
+ return null;
16192
+ }
16193
+ return {
16194
+ latitude,
16195
+ longitude,
16196
+ fallbackCity: asCity(geo?.city),
16197
+ };
16198
+ }
16199
+ async function tryBrowserGeolocation() {
16200
+ if (typeof navigator === 'undefined' ||
16201
+ typeof navigator.geolocation?.getCurrentPosition !== 'function') {
16202
+ return null;
16203
+ }
16204
+ return await new Promise((resolve) => {
16205
+ const timer = setTimeout(() => resolve(null), BROWSER_GEO_MS);
16206
+ navigator.geolocation.getCurrentPosition((position) => {
16207
+ clearTimeout(timer);
16208
+ resolve({
16209
+ latitude: position.coords.latitude,
16210
+ longitude: position.coords.longitude,
16211
+ });
16212
+ }, () => {
16213
+ clearTimeout(timer);
16214
+ resolve(null);
16215
+ }, {
16216
+ enableHighAccuracy: false,
16217
+ maximumAge: 600_000,
16218
+ timeout: BROWSER_GEO_MS,
16219
+ });
16220
+ });
16221
+ }
16222
+ async function reverseGeocodeCity(fetchFn, latitude, longitude) {
16223
+ const url = `${REVERSE_GEO_URL}?latitude=${encodeURIComponent(String(latitude))}` +
16224
+ `&longitude=${encodeURIComponent(String(longitude))}` +
16225
+ `&localityLanguage=en`;
16226
+ const payload = (await fetchJson(fetchFn, url));
16227
+ return (asCity(payload?.city) ??
16228
+ asCity(payload?.locality) ??
16229
+ asCity(payload?.principalSubdivision));
16230
+ }
15948
16231
  function asNumber(value) {
15949
16232
  if (typeof value === 'number' && Number.isFinite(value)) {
15950
16233
  return value;
@@ -16166,7 +16449,7 @@ class AppShellHeaderComponent {
16166
16449
  this.notificationsDrawer
16167
16450
  .open({
16168
16451
  title: this.notificationsTitle(),
16169
- notifications: onLoadPage ? undefined : this.notifications(),
16452
+ notifications: this.notifications(),
16170
16453
  onLoadPage,
16171
16454
  onMarkRead,
16172
16455
  onMarkAllRead,
@@ -16563,8 +16846,8 @@ class AppShellComponent {
16563
16846
  hostClass = computed(() => this.layoutPreview() ? 'block h-full' : 'block min-h-full', /* @ts-ignore */
16564
16847
  ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
16565
16848
  shellRootClass = computed(() => this.layoutPreview()
16566
- ? 'relative flex h-full min-h-0'
16567
- : 'relative flex min-h-full', /* @ts-ignore */
16849
+ ? 'relative flex h-full min-h-0 bg-background-chrome dark:bg-ink-950'
16850
+ : 'relative flex min-h-full bg-background-chrome dark:bg-ink-950', /* @ts-ignore */
16568
16851
  ...(ngDevMode ? [{ debugName: "shellRootClass" }] : /* istanbul ignore next */ []));
16569
16852
  headerDensity = computed(() => {
16570
16853
  const preview = this.layoutPreview();
@@ -17168,7 +17451,7 @@ class PaginationComponent {
17168
17451
  <button
17169
17452
  africanies-button
17170
17453
  type="button"
17171
- variant="secondary"
17454
+ variant="flat"
17172
17455
  size="sm"
17173
17456
  [disabled]="controlsDisabled() || !meta().has_previous_page"
17174
17457
  (click)="emitPage(meta().current_page - 1)"
@@ -17189,7 +17472,7 @@ class PaginationComponent {
17189
17472
  type="button"
17190
17473
  size="sm"
17191
17474
  class="min-w-8"
17192
- [variant]="item === meta().current_page ? 'primary' : 'secondary'"
17475
+ [variant]="item === meta().current_page ? 'primary' : 'flat'"
17193
17476
  [disabled]="controlsDisabled()"
17194
17477
  [attr.aria-label]="'Page ' + item"
17195
17478
  [attr.aria-current]="
@@ -17204,7 +17487,7 @@ class PaginationComponent {
17204
17487
  <button
17205
17488
  africanies-button
17206
17489
  type="button"
17207
- variant="secondary"
17490
+ variant="flat"
17208
17491
  size="sm"
17209
17492
  [disabled]="controlsDisabled() || !meta().has_next_page"
17210
17493
  (click)="emitPage(meta().current_page + 1)"
@@ -17269,7 +17552,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17269
17552
  <button
17270
17553
  africanies-button
17271
17554
  type="button"
17272
- variant="secondary"
17555
+ variant="flat"
17273
17556
  size="sm"
17274
17557
  [disabled]="controlsDisabled() || !meta().has_previous_page"
17275
17558
  (click)="emitPage(meta().current_page - 1)"
@@ -17290,7 +17573,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17290
17573
  type="button"
17291
17574
  size="sm"
17292
17575
  class="min-w-8"
17293
- [variant]="item === meta().current_page ? 'primary' : 'secondary'"
17576
+ [variant]="item === meta().current_page ? 'primary' : 'flat'"
17294
17577
  [disabled]="controlsDisabled()"
17295
17578
  [attr.aria-label]="'Page ' + item"
17296
17579
  [attr.aria-current]="
@@ -17305,7 +17588,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17305
17588
  <button
17306
17589
  africanies-button
17307
17590
  type="button"
17308
- variant="secondary"
17591
+ variant="flat"
17309
17592
  size="sm"
17310
17593
  [disabled]="controlsDisabled() || !meta().has_next_page"
17311
17594
  (click)="emitPage(meta().current_page + 1)"
@@ -17390,9 +17673,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17390
17673
  * **Sticky columns:** a column with `key: 'actions'` stays pinned to the
17391
17674
  * right while the grid scrolls horizontally. The expand trigger column pins
17392
17675
  * to the left when rows are expandable. Override with
17393
- * {@link TableColumn.sticky}.
17676
+ * {@link TableColumn.sticky}. Sticky columns use a slightly thicker bottom
17677
+ * border (and opaque fill) so dividers stay visible while scrolling.
17394
17678
  */
17395
17679
  class TableComponent {
17680
+ modeColor = inject(ModeColorService);
17396
17681
  /**
17397
17682
  * When false, hides row expansion even if {@link RowDetailDefDirective}
17398
17683
  * templates are projected. Defaults to true.
@@ -17602,6 +17887,21 @@ class TableComponent {
17602
17887
  cellContext(row) {
17603
17888
  return { $implicit: row };
17604
17889
  }
17890
+ /**
17891
+ * Header row chrome. Soft fill lives on each `th` ({@link headerSoftClass})
17892
+ * so sticky columns do not double-tint and paint over neighbor labels.
17893
+ * @returns Header row class list.
17894
+ */
17895
+ headerRowClass() {
17896
+ return 'border-b border-border dark:border-white/10';
17897
+ }
17898
+ /**
17899
+ * Opaque mode soft fill for header cells (sticky-safe in dark mode).
17900
+ * @returns Soft solid background utilities.
17901
+ */
17902
+ headerSoftClass() {
17903
+ return this.modeColor.classes().softSolid;
17904
+ }
17605
17905
  /**
17606
17906
  * Body row classes — row marker for hover styles, plus hide cell borders on
17607
17907
  * the last / expanded row.
@@ -17612,7 +17912,7 @@ class TableComponent {
17612
17912
  bodyRowClass(row, index) {
17613
17913
  if (this.isExpandable() && this.isRowExpanded(row, index)) {
17614
17914
  // Only the bottom edge — keep the top divider so the row still
17615
- // separates from the row above.
17915
+ // separates from the row above. Detail panel owns the separator.
17616
17916
  return 'africanies-table-row group [&>td]:border-b-0';
17617
17917
  }
17618
17918
  return 'africanies-table-row group last:[&>td]:border-b-0';
@@ -17638,21 +17938,27 @@ class TableComponent {
17638
17938
  * @returns Header class list.
17639
17939
  */
17640
17940
  expandHeaderClass() {
17641
- return ('w-10 border-b border-border px-2 py-3.5 dark:border-white/10 ' +
17941
+ return ('w-10 border-b-[1.5px] border-border px-2 py-4 dark:border-white/10 ' +
17942
+ this.headerSoftClass() +
17943
+ ' ' +
17642
17944
  this.stickyPinClass('left', 'head'));
17643
17945
  }
17644
17946
  /**
17645
17947
  * Body classes for the expand/collapse trigger column (pinned left).
17646
- * Omits `border-b` like other sticky cells so the opaque fill does not
17647
- * double-paint the row divider.
17948
+ * Uses a slightly thicker bottom border than scrolling cells so the
17949
+ * sticky fill still reads a clear divider (no top-shadow fallback).
17648
17950
  * @param index - Index within {@link rows}.
17649
17951
  * @returns Body cell class list.
17650
17952
  */
17651
17953
  expandBodyClass(index) {
17652
- const prev = index > 0 ? this.rowList()[index - 1] : undefined;
17653
- const afterExpanded = prev !== undefined && this.isRowExpanded(prev, index - 1);
17654
- return ('w-10 px-2 py-3.5 align-middle ' +
17655
- this.stickyPinClass('left', 'body', index, afterExpanded));
17954
+ const row = this.rowList()[index];
17955
+ const expanded = row !== undefined && this.isRowExpanded(row, index);
17956
+ const border = expanded
17957
+ ? ''
17958
+ : 'border-b-[1.5px] border-border dark:border-white/10 ';
17959
+ return ('w-10 px-2 py-4 align-middle ' +
17960
+ border +
17961
+ this.stickyPinClass('left', 'body'));
17656
17962
  }
17657
17963
  /**
17658
17964
  * Header cell classes, including sticky pin when set.
@@ -17660,69 +17966,71 @@ class TableComponent {
17660
17966
  * @returns Header class list.
17661
17967
  */
17662
17968
  headerCellClass(col) {
17663
- return ('border-b border-border whitespace-normal wrap-break-word px-3 py-3.5 font-medium text-neutral-600 dark:border-white/10 dark:text-neutral-400 ' +
17969
+ const sticky = this.stickyEdge(col);
17970
+ const border = sticky
17971
+ ? 'border-b-[1.5px] '
17972
+ : 'border-b ';
17973
+ return (border +
17974
+ 'border-border whitespace-normal wrap-break-word px-[1.125rem] py-4 font-medium text-neutral-600 dark:border-white/10 dark:text-neutral-400 ' +
17975
+ this.headerSoftClass() +
17976
+ ' ' +
17664
17977
  this.stickySurfaceClass(col, 'head'));
17665
17978
  }
17666
17979
  /**
17667
17980
  * Body cell classes, including sticky pin when set.
17668
- * Sticky cells omit `border-b`: their opaque fill sits in a higher stacking
17669
- * context, so a bottom border plus the next row's top shadow paint on the
17670
- * same pixel and read brighter (especially in dark mode).
17981
+ * Sticky expand/actions columns use a 1.5px bottom border; scrolling cells
17982
+ * stay at 1px. Expanded rows drop the bottom border (see {@link bodyRowClass}).
17671
17983
  * @param col - Column definition.
17672
17984
  * @param index - Index within {@link rows}.
17673
17985
  * @returns Body cell class list.
17674
17986
  */
17675
17987
  bodyCellClass(col, index) {
17676
- const rowLine = this.stickyEdge(col)
17988
+ const row = this.rowList()[index];
17989
+ const expanded = row !== undefined && this.isRowExpanded(row, index);
17990
+ const sticky = this.stickyEdge(col);
17991
+ const rowLine = expanded
17677
17992
  ? ''
17678
- : 'border-b border-border dark:border-white/10 ';
17679
- const prev = index > 0 ? this.rowList()[index - 1] : undefined;
17680
- const afterExpanded = prev !== undefined && this.isRowExpanded(prev, index - 1);
17993
+ : sticky
17994
+ ? 'border-b-[1.5px] border-border dark:border-white/10 '
17995
+ : 'border-b border-border dark:border-white/10 ';
17681
17996
  const wrap = col.key === 'actions'
17682
17997
  ? 'whitespace-nowrap '
17683
17998
  : 'whitespace-normal wrap-break-word ';
17684
17999
  return (rowLine +
17685
- 'px-3 py-3.5 align-middle ' +
18000
+ 'px-[1.125rem] py-4 align-middle ' +
17686
18001
  wrap +
17687
- this.stickySurfaceClass(col, 'body', index, afterExpanded));
18002
+ this.stickySurfaceClass(col, 'body'));
17688
18003
  }
17689
18004
  /**
17690
18005
  * Sticky pin utilities for a known edge (expand column or data column).
17691
18006
  * @param edge - Left or right pin.
17692
18007
  * @param surface - Header vs body fill tokens.
17693
- * @param index - Body row index; unused for the header.
17694
- * @param afterExpanded - Previous row is expanded — skip the top shadow
17695
- * divider so the sticky column stays flush under the detail panel.
17696
18008
  * @returns Sticky utilities.
17697
18009
  */
17698
- stickyPinClass(edge, surface, index = 0, afterExpanded = false) {
18010
+ stickyPinClass(edge, surface) {
17699
18011
  // Expand (left) sits above scrolling cells; actions (right) match.
17700
18012
  // Header needs a higher z so it stays above sticky body cells.
17701
18013
  const side = edge === 'right' ? 'sticky right-0 z-[1]' : 'sticky left-0 z-[1]';
17702
18014
  if (surface === 'head') {
17703
- return `${side} z-[2] bg-background-welcome dark:bg-ink-950`;
18015
+ // Fill comes from {@link headerSoftClass} on the cell — do not re-tint.
18016
+ return `${side} z-[2]`;
17704
18017
  }
17705
- const rowDivider = !afterExpanded && index > 0
17706
- ? 'shadow-[0_-1px_0_0_#c9d5e1] dark:shadow-[0_-1px_0_0_rgba(255,255,255,0.1)]'
17707
- : '';
17708
- const fill = 'bg-white dark:bg-ink';
17709
- return `${side} ${fill} ${rowDivider}`;
18018
+ const fill = 'bg-white dark:bg-ink-surface';
18019
+ return `${side} ${fill}`;
17710
18020
  }
17711
18021
  /**
17712
18022
  * Sticky pin plus an opaque fill matching the table surface, so scrolled
17713
18023
  * cells do not show through.
17714
18024
  * @param col - Column definition.
17715
18025
  * @param surface - Header vs body fill tokens.
17716
- * @param index - Body row index; unused for the header.
17717
- * @param afterExpanded - Previous row is expanded — skip the top shadow.
17718
18026
  * @returns Sticky utilities, or empty when the column scrolls.
17719
18027
  */
17720
- stickySurfaceClass(col, surface, index = 0, afterExpanded = false) {
18028
+ stickySurfaceClass(col, surface) {
17721
18029
  const edge = this.stickyEdge(col);
17722
18030
  if (!edge) {
17723
18031
  return '';
17724
18032
  }
17725
- return this.stickyPinClass(edge, surface, index, afterExpanded);
18033
+ return this.stickyPinClass(edge, surface);
17726
18034
  }
17727
18035
  /**
17728
18036
  * Stable id for a row — used for expand/collapse state.
@@ -17825,7 +18133,7 @@ class TableComponent {
17825
18133
  <button
17826
18134
  africanies-button
17827
18135
  type="button"
17828
- variant="secondary"
18136
+ variant="flat"
17829
18137
  size="sm"
17830
18138
  [disabled]="loading() || refreshing()"
17831
18139
  [attr.aria-label]="refreshLabel()"
@@ -17845,7 +18153,7 @@ class TableComponent {
17845
18153
  <button
17846
18154
  africanies-button
17847
18155
  type="button"
17848
- variant="secondary"
18156
+ variant="flat"
17849
18157
  size="sm"
17850
18158
  [attr.aria-label]="filterLabel()"
17851
18159
  (click)="filterClick.emit()"
@@ -17865,7 +18173,7 @@ class TableComponent {
17865
18173
  <button
17866
18174
  africanies-button
17867
18175
  type="button"
17868
- variant="secondary"
18176
+ variant="flat"
17869
18177
  size="sm"
17870
18178
  [attr.aria-label]="exportLabel()"
17871
18179
  (click)="exportClick.emit()"
@@ -17891,14 +18199,12 @@ class TableComponent {
17891
18199
  }
17892
18200
 
17893
18201
  <div
17894
- class="min-w-0 w-full overflow-x-auto rounded-md border border-border bg-white dark:border-white/10 dark:bg-ink"
18202
+ class="min-w-0 w-full overflow-x-auto rounded-md border border-border bg-white dark:border-white/10 dark:bg-ink-surface"
17895
18203
  >
17896
18204
  <table
17897
18205
  class="w-max min-w-full table-auto border-separate border-spacing-0 bg-inherit text-left text-body text-ink dark:text-white"
17898
18206
  >
17899
- <thead
17900
- class="border-b border-border bg-background-welcome dark:border-white/10 dark:bg-ink-950"
17901
- >
18207
+ <thead [class]="headerRowClass()">
17902
18208
  <tr>
17903
18209
  @if (isExpandable()) {
17904
18210
  <th
@@ -17962,14 +18268,14 @@ class TableComponent {
17962
18268
  @switch (bodyKind()) {
17963
18269
  @case ('loading') {
17964
18270
  <tr>
17965
- <td class="px-3 py-6" [attr.colspan]="colSpan()">
18271
+ <td class="px-[1.125rem] py-6" [attr.colspan]="colSpan()">
17966
18272
  <africanies-loading-state [message]="loadingLabel()" />
17967
18273
  </td>
17968
18274
  </tr>
17969
18275
  }
17970
18276
  @case ('error') {
17971
18277
  <tr>
17972
- <td class="px-3 py-6" [attr.colspan]="colSpan()">
18278
+ <td class="px-[1.125rem] py-6" [attr.colspan]="colSpan()">
17973
18279
  <africanies-error-state
17974
18280
  [message]="errorMessage()"
17975
18281
  [refreshing]="refreshing()"
@@ -17980,7 +18286,7 @@ class TableComponent {
17980
18286
  }
17981
18287
  @case ('empty') {
17982
18288
  <tr>
17983
- <td class="px-3 py-6" [attr.colspan]="colSpan()">
18289
+ <td class="px-[1.125rem] py-6" [attr.colspan]="colSpan()">
17984
18290
  <africanies-empty-state
17985
18291
  [message]="emptyMessage()"
17986
18292
  [refreshing]="refreshing()"
@@ -18048,7 +18354,7 @@ class TableComponent {
18048
18354
  class="min-h-0 overflow-hidden border-t border-border/70 bg-neutral-50 dark:border-white/10 dark:bg-white/[0.05]"
18049
18355
  >
18050
18356
  <dl
18051
- class="m-0 grid gap-x-6 gap-y-3 px-3 py-3 transition-opacity duration-200 ease-out motion-reduce:transition-none sm:grid-cols-2 lg:grid-cols-3"
18357
+ 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"
18052
18358
  [class.opacity-0]="!isRowExpanded(row, i)"
18053
18359
  [class.opacity-100]="isRowExpanded(row, i)"
18054
18360
  >
@@ -18119,7 +18425,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18119
18425
  <button
18120
18426
  africanies-button
18121
18427
  type="button"
18122
- variant="secondary"
18428
+ variant="flat"
18123
18429
  size="sm"
18124
18430
  [disabled]="loading() || refreshing()"
18125
18431
  [attr.aria-label]="refreshLabel()"
@@ -18139,7 +18445,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18139
18445
  <button
18140
18446
  africanies-button
18141
18447
  type="button"
18142
- variant="secondary"
18448
+ variant="flat"
18143
18449
  size="sm"
18144
18450
  [attr.aria-label]="filterLabel()"
18145
18451
  (click)="filterClick.emit()"
@@ -18159,7 +18465,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18159
18465
  <button
18160
18466
  africanies-button
18161
18467
  type="button"
18162
- variant="secondary"
18468
+ variant="flat"
18163
18469
  size="sm"
18164
18470
  [attr.aria-label]="exportLabel()"
18165
18471
  (click)="exportClick.emit()"
@@ -18185,14 +18491,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18185
18491
  }
18186
18492
 
18187
18493
  <div
18188
- class="min-w-0 w-full overflow-x-auto rounded-md border border-border bg-white dark:border-white/10 dark:bg-ink"
18494
+ class="min-w-0 w-full overflow-x-auto rounded-md border border-border bg-white dark:border-white/10 dark:bg-ink-surface"
18189
18495
  >
18190
18496
  <table
18191
18497
  class="w-max min-w-full table-auto border-separate border-spacing-0 bg-inherit text-left text-body text-ink dark:text-white"
18192
18498
  >
18193
- <thead
18194
- class="border-b border-border bg-background-welcome dark:border-white/10 dark:bg-ink-950"
18195
- >
18499
+ <thead [class]="headerRowClass()">
18196
18500
  <tr>
18197
18501
  @if (isExpandable()) {
18198
18502
  <th
@@ -18256,14 +18560,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18256
18560
  @switch (bodyKind()) {
18257
18561
  @case ('loading') {
18258
18562
  <tr>
18259
- <td class="px-3 py-6" [attr.colspan]="colSpan()">
18563
+ <td class="px-[1.125rem] py-6" [attr.colspan]="colSpan()">
18260
18564
  <africanies-loading-state [message]="loadingLabel()" />
18261
18565
  </td>
18262
18566
  </tr>
18263
18567
  }
18264
18568
  @case ('error') {
18265
18569
  <tr>
18266
- <td class="px-3 py-6" [attr.colspan]="colSpan()">
18570
+ <td class="px-[1.125rem] py-6" [attr.colspan]="colSpan()">
18267
18571
  <africanies-error-state
18268
18572
  [message]="errorMessage()"
18269
18573
  [refreshing]="refreshing()"
@@ -18274,7 +18578,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18274
18578
  }
18275
18579
  @case ('empty') {
18276
18580
  <tr>
18277
- <td class="px-3 py-6" [attr.colspan]="colSpan()">
18581
+ <td class="px-[1.125rem] py-6" [attr.colspan]="colSpan()">
18278
18582
  <africanies-empty-state
18279
18583
  [message]="emptyMessage()"
18280
18584
  [refreshing]="refreshing()"
@@ -18342,7 +18646,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18342
18646
  class="min-h-0 overflow-hidden border-t border-border/70 bg-neutral-50 dark:border-white/10 dark:bg-white/[0.05]"
18343
18647
  >
18344
18648
  <dl
18345
- class="m-0 grid gap-x-6 gap-y-3 px-3 py-3 transition-opacity duration-200 ease-out motion-reduce:transition-none sm:grid-cols-2 lg:grid-cols-3"
18649
+ 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"
18346
18650
  [class.opacity-0]="!isRowExpanded(row, i)"
18347
18651
  [class.opacity-100]="isRowExpanded(row, i)"
18348
18652
  >
@@ -18763,6 +19067,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18763
19067
  }] });
18764
19068
 
18765
19069
  const FEEDBACK = [
19070
+ AccordionComponent,
18766
19071
  LoadingStateComponent,
18767
19072
  ErrorStateComponent,
18768
19073
  ErrorIndicatorComponent,
@@ -18772,7 +19077,7 @@ const FEEDBACK = [
18772
19077
  ChipComponent,
18773
19078
  ];
18774
19079
  /**
18775
- * Async feedback states, inline alert, and status chips.
19080
+ * Async feedback states, accordion panels, inline alert, and status chips.
18776
19081
  *
18777
19082
  * Toast stays out of this module — register {@link provideAfricaniesToasts} instead.
18778
19083
  *
@@ -18786,13 +19091,15 @@ const FEEDBACK = [
18786
19091
  */
18787
19092
  class AfricaniesFeedbackModule {
18788
19093
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AfricaniesFeedbackModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
18789
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: AfricaniesFeedbackModule, imports: [LoadingStateComponent,
19094
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: AfricaniesFeedbackModule, imports: [AccordionComponent,
19095
+ LoadingStateComponent,
18790
19096
  ErrorStateComponent,
18791
19097
  ErrorIndicatorComponent,
18792
19098
  EmptyStateComponent,
18793
19099
  AsyncStateComponent,
18794
19100
  AlertComponent,
18795
- ChipComponent], exports: [LoadingStateComponent,
19101
+ ChipComponent], exports: [AccordionComponent,
19102
+ LoadingStateComponent,
18796
19103
  ErrorStateComponent,
18797
19104
  ErrorIndicatorComponent,
18798
19105
  EmptyStateComponent,
@@ -19084,5 +19391,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
19084
19391
  * Generated bundle index. Do not edit.
19085
19392
  */
19086
19393
 
19087
- export { AFRICANIES_BRAND_LOGO_MINI_URL, AFRICANIES_BRAND_LOGO_URL, ActionMenuComponent, ActionMenuTriggerDirective, AddressInputComponent, AfricaniesActionsModule, AfricaniesFeedbackModule, AfricaniesFormsModule, AfricaniesInfoPopoverModule, AfricaniesNavigationModule, AfricaniesOverlayRef, AfricaniesStepperModule, AfricaniesTableModule, AfricaniesTooltipModule, AlertComponent, AppShellComponent, AppShellContentHeaderComponent, AppShellHeaderComponent, AppShellHeaderEndDirective, AppShellHeaderSlotDirective, AppShellHeaderStartDirective, AsyncStateComponent, AvatarComponent, AvatarMenuComponent, BrandLogoComponent, BreadcrumbComponent, ButtonComponent, CarrierLogoComponent, CellDefDirective, CheckboxComponent, ChipComponent, ConfirmDialogComponent, ConfirmService, ContentStackComponent, CopyButtonComponent, DatePickerComponent, DrawerService, EmptyStateComponent, ErrorIndicatorComponent, ErrorStateComponent, FileUploadComponent, FilterDrawerPanel, FilterDrawerService, FilterQueryService, GOOGLE_PLACES_CONFIG, GooglePlacesService, HeaderCellDefDirective, ImageComponent, InfoPopoverComponent, InfoPopoverContentDirective, InfoPopoverTriggerDirective, LoadingStateComponent, MODAL_SIZE_PANEL_CLASS, ModalService, NotificationDrawerPanel, NotificationDrawerService, NumberInputComponent, OVERLAY_DATA, OtpInputComponent, OverlayFooterDirective, OverlayFrameComponent, OverlayHeaderDirective, PageHeaderComponent, PaginationComponent, RadioComponent, RowDetailDefDirective, SearchComboboxComponent, SegmentComponent, SelectComponent, ShippingModeSwitchComponent, SideNavComponent, StepDefDirective, StepperComponent, TOAST_DURATION_MS, TOAST_ICONS, TabDefDirective, TableComponent, TabsComponent, TextInputComponent, TextareaComponent, ToastService, ToggleComponent, TooltipComponent, TooltipTriggerDirective, acceptLabels, buildBreadcrumbsFromSideNav, deliveryVendorOptionsForStoredValue, deliveryVendorSelectOptions, deliveryVendorSelected, fileExtensionLabel, fileMatchesAccept, headerGreetingFirstName, headerGreetingPeriod, isCatalogRootRoute, isNavItemActive, isNestedChildRoute, navItemUrlTree, normalizeCarrierLogoSlug, normalizeNavPath, pickHeaderGreeting, provideAfricaniesToasts, provideAfricaniesUiOverlays, provideGooglePlaces, resolveCatalogRootLink, resolveContentBackTarget, resolveHeaderBackTarget, resolveParentPathFromRootSnapshot, toastFingerprint };
19394
+ export { AFRICANIES_BRAND_LOGO_MINI_URL, AFRICANIES_BRAND_LOGO_URL, AccordionComponent, ActionMenuComponent, ActionMenuTriggerDirective, AddressInputComponent, AfricaniesActionsModule, AfricaniesFeedbackModule, AfricaniesFormsModule, AfricaniesInfoPopoverModule, AfricaniesNavigationModule, AfricaniesOverlayRef, AfricaniesStepperModule, AfricaniesTableModule, AfricaniesTooltipModule, AlertComponent, AppShellComponent, AppShellContentHeaderComponent, AppShellHeaderComponent, AppShellHeaderEndDirective, AppShellHeaderSlotDirective, AppShellHeaderStartDirective, AsyncStateComponent, AvatarComponent, AvatarMenuComponent, BrandLogoComponent, BreadcrumbComponent, ButtonComponent, CarrierLogoComponent, CellDefDirective, CheckboxComponent, ChipComponent, ConfirmDialogComponent, ConfirmService, ContentStackComponent, CopyButtonComponent, DatePickerComponent, DrawerService, EmptyStateComponent, ErrorIndicatorComponent, ErrorStateComponent, FileUploadComponent, FilterDrawerPanel, FilterDrawerService, FilterQueryService, GOOGLE_PLACES_CONFIG, GooglePlacesService, HeaderCellDefDirective, ImageComponent, InfoPopoverComponent, InfoPopoverContentDirective, InfoPopoverTriggerDirective, LoadingStateComponent, MODAL_SIZE_PANEL_CLASS, ModalService, NotificationDrawerPanel, NotificationDrawerService, NumberInputComponent, OVERLAY_DATA, OtpInputComponent, OverlayFooterDirective, OverlayFrameComponent, OverlayHeaderDirective, PageHeaderComponent, PaginationComponent, RadioComponent, RowDetailDefDirective, SearchComboboxComponent, SegmentComponent, SelectComponent, ShippingModeSwitchComponent, SideNavComponent, StepDefDirective, StepperComponent, TOAST_DURATION_MS, TOAST_ICONS, TabDefDirective, TableComponent, TabsComponent, TextInputComponent, TextareaComponent, ToastService, ToggleComponent, TooltipComponent, TooltipTriggerDirective, acceptLabels, buildBreadcrumbsFromSideNav, deliveryVendorOptionsForStoredValue, deliveryVendorSelectOptions, deliveryVendorSelected, fileExtensionLabel, fileMatchesAccept, headerGreetingFirstName, headerGreetingPeriod, isCatalogRootRoute, isNavItemActive, isNestedChildRoute, navItemUrlTree, normalizeCarrierLogoSlug, normalizeNavPath, pickHeaderGreeting, provideAfricaniesToasts, provideAfricaniesUiOverlays, provideGooglePlaces, resolveCatalogRootLink, resolveContentBackTarget, resolveHeaderBackTarget, resolveParentPathFromRootSnapshot, toastFingerprint };
19088
19395
  //# sourceMappingURL=africanies-africanies-ui.mjs.map