@africanies/angular-web-sdk 0.1.8 → 0.1.10

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 (91) hide show
  1. package/.angular/cache/22.0.9/playground/.tsbuildinfo +1 -1
  2. package/.nx/cache/1149535958171016935/dist/libs/africanies-theme/README.md +46 -0
  3. package/.nx/cache/1149535958171016935/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +243 -0
  4. package/.nx/cache/1149535958171016935/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -0
  5. package/.nx/cache/1149535958171016935/dist/libs/africanies-theme/package.json +35 -0
  6. package/.nx/cache/1149535958171016935/dist/libs/africanies-theme/tailwind-preset.cjs +330 -0
  7. package/.nx/cache/1149535958171016935/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +178 -0
  8. package/.nx/cache/17289624717336768573/dist/libs/africanies-core/README.md +85 -0
  9. package/.nx/cache/17289624717336768573/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +4639 -0
  10. package/.nx/cache/17289624717336768573/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +1 -0
  11. package/.nx/cache/17289624717336768573/dist/libs/africanies-core/package.json +31 -0
  12. package/.nx/cache/17289624717336768573/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +2748 -0
  13. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/README.md +68 -0
  14. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
  15. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
  16. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +5 -0
  17. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/assets/carriers/dhl.svg +7 -0
  18. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +19998 -0
  19. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -0
  20. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/package.json +35 -0
  21. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +5616 -0
  22. package/.nx/cache/3953970051755345887/dist/libs/africanies-ui/README.md +68 -0
  23. package/.nx/cache/3953970051755345887/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
  24. package/.nx/cache/3953970051755345887/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
  25. package/.nx/cache/3953970051755345887/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +5 -0
  26. package/.nx/cache/3953970051755345887/dist/libs/africanies-ui/assets/carriers/dhl.svg +7 -0
  27. package/.nx/cache/3953970051755345887/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +19500 -0
  28. package/.nx/cache/3953970051755345887/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -0
  29. package/.nx/cache/3953970051755345887/dist/libs/africanies-ui/package.json +35 -0
  30. package/.nx/cache/3953970051755345887/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +5521 -0
  31. package/.nx/cache/4225324930345532464/dist/libs/africanies-theme/README.md +46 -0
  32. package/.nx/cache/4225324930345532464/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +243 -0
  33. package/.nx/cache/4225324930345532464/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -0
  34. package/.nx/cache/4225324930345532464/dist/libs/africanies-theme/package.json +35 -0
  35. package/.nx/cache/4225324930345532464/dist/libs/africanies-theme/tailwind-preset.cjs +330 -0
  36. package/.nx/cache/4225324930345532464/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +176 -0
  37. package/.nx/cache/run.json +26 -26
  38. package/.nx/cache/terminalOutputs/1149535958171016935 +21 -0
  39. package/.nx/cache/terminalOutputs/12565224028063674529 +37 -0
  40. package/.nx/cache/terminalOutputs/13478445034325959094 +176 -0
  41. package/.nx/cache/terminalOutputs/15835338512221847842 +184 -182
  42. package/.nx/cache/terminalOutputs/17289624717336768573 +21 -0
  43. package/.nx/cache/terminalOutputs/17571479482482478257 +35 -0
  44. package/.nx/cache/terminalOutputs/3088264223473143358 +8 -0
  45. package/.nx/cache/terminalOutputs/3735952477975556635 +21 -0
  46. package/.nx/cache/terminalOutputs/3953970051755345887 +21 -0
  47. package/.nx/cache/terminalOutputs/4225324930345532464 +21 -0
  48. package/.nx/cache/terminalOutputs/4363587767476846758 +176 -0
  49. package/.nx/cache/terminalOutputs/8193462679279018087 +39 -0
  50. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db +0 -0
  51. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-shm +0 -0
  52. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-wal +0 -0
  53. package/.nx/workspace-data/d/daemon.log +5890 -0
  54. package/.nx/workspace-data/d/server-process.json +2 -2
  55. package/.nx/workspace-data/file-map.json +3610 -3620
  56. package/.nx/workspace-data/nx_files.nxt +0 -0
  57. package/.nx/workspace-data/project-graph.json +2 -2
  58. package/apps/playground/src/app/pages/chip-page.ts +11 -1
  59. package/apps/playground/src/app/pages/tokens-page.ts +2 -2
  60. package/apps/playground/src/app/snippets/chip.snippets.ts +5 -0
  61. package/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +6 -1
  62. package/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +1 -1
  63. package/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +6 -1
  64. package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +8 -8
  65. package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -1
  66. package/dist/libs/africanies-theme/tailwind-preset.cjs +3 -3
  67. package/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +5 -3
  68. package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +848 -375
  69. package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -1
  70. package/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +123 -30
  71. package/libs/africanies-core/src/index.ts +1 -1
  72. package/libs/africanies-core/src/lib/notification/notification-link.ts +6 -1
  73. package/libs/africanies-theme/src/lib/mode-color.safelist.ts +2 -2
  74. package/libs/africanies-theme/src/lib/mode-color.service.spec.ts +9 -7
  75. package/libs/africanies-theme/src/lib/mode-color.service.ts +9 -7
  76. package/libs/africanies-theme/tailwind-preset.cjs +3 -3
  77. package/libs/africanies-ui/src/lib/avatar/avatar-menu.component.ts +25 -21
  78. package/libs/africanies-ui/src/lib/button/button.component.spec.ts +13 -8
  79. package/libs/africanies-ui/src/lib/chip/chip.component.ts +74 -6
  80. package/libs/africanies-ui/src/lib/layout/app-shell-header.component.ts +39 -16
  81. package/libs/africanies-ui/src/lib/layout/header-greeting.util.ts +1 -1
  82. package/libs/africanies-ui/src/lib/layout/header-weather.spec.ts +87 -5
  83. package/libs/africanies-ui/src/lib/layout/header-weather.ts +35 -15
  84. package/libs/africanies-ui/src/lib/navigation/segment/segment.component.ts +98 -22
  85. package/libs/africanies-ui/src/lib/navigation/shipping-mode-switch/shipping-mode-switch.component.ts +193 -65
  86. package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.html +4 -2
  87. package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.ts +3 -1
  88. package/libs/africanies-ui/src/lib/table/table.component.spec.ts +3 -0
  89. package/libs/africanies-ui/src/lib/table/table.component.ts +197 -57
  90. package/package.json +1 -1
  91. package/tools/build-icon-sprite.mjs +1 -1
@@ -2860,19 +2860,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2860
2860
  * Compact status / category label.
2861
2861
  *
2862
2862
  * Soft fills stay readable in dark mode (translucent accent tints, not
2863
- * light-only subtle backgrounds).
2863
+ * light-only subtle backgrounds). Pass {@link color} for a custom accent —
2864
+ * text uses that color and the background is a faded mix of the same.
2864
2865
  *
2865
2866
  * @example
2866
2867
  * ```html
2867
2868
  * <africanies-chip variant="success">Delivered</africanies-chip>
2868
- * <africanies-chip variant="import" icon="truck">In transit</africanies-chip>
2869
+ * <africanies-chip variant="violet">Priority</africanies-chip>
2870
+ * <africanies-chip color="#0f766e">Pay Later</africanies-chip>
2869
2871
  * <africanies-chip variant="warning" [removable]="true" (removed)="clear()">Pending</africanies-chip>
2870
2872
  * ```
2871
2873
  */
2872
2874
  class ChipComponent {
2873
- /** Semantic tone. Defaults to `neutral`. */
2875
+ /** Semantic tone. Defaults to `neutral`. Ignored when {@link color} is set. */
2874
2876
  variant = input('neutral', /* @ts-ignore */
2875
2877
  ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
2878
+ /**
2879
+ * Optional CSS color (`#7c3aed`, `rgb(…)`, `oklch(…)`, …). When set, overrides
2880
+ * {@link variant}: label uses the color and the fill is a faded mix of it.
2881
+ */
2882
+ color = input(null, /* @ts-ignore */
2883
+ ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
2876
2884
  /** Padding / type scale. Defaults to `sm` (table / dense UI). */
2877
2885
  size = input('sm', /* @ts-ignore */
2878
2886
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
@@ -2891,6 +2899,12 @@ class ChipComponent {
2891
2899
  removed = output();
2892
2900
  iconSize = computed(() => (this.size() === 'md' ? 14 : 12), /* @ts-ignore */
2893
2901
  ...(ngDevMode ? [{ debugName: "iconSize" }] : /* istanbul ignore next */ []));
2902
+ /** Trimmed custom accent, or `null` when using a named {@link variant}. */
2903
+ customAccent = computed(() => {
2904
+ const value = this.color()?.trim() ?? '';
2905
+ return value.length > 0 ? value : null;
2906
+ }, /* @ts-ignore */
2907
+ ...(ngDevMode ? [{ debugName: "customAccent" }] : /* istanbul ignore next */ []));
2894
2908
  hostClass = computed(() => {
2895
2909
  const base = 'inline-flex max-w-full items-center gap-1 rounded-md font-sans font-medium ' +
2896
2910
  'border border-transparent';
@@ -2902,11 +2916,15 @@ class ChipComponent {
2902
2916
  }, /* @ts-ignore */
2903
2917
  ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
2904
2918
  /**
2905
- * Soft background + text per variant. Dark mode uses translucent accents so
2906
- * chips never wash out as near-white blobs.
2907
- * @returns Tailwind class string for the active variant.
2919
+ * Soft background + text per variant, or the custom-color host class.
2920
+ * Dark mode uses translucent accents so chips never wash out as near-white
2921
+ * blobs.
2922
+ * @returns Tailwind / host class string for the active tone.
2908
2923
  */
2909
2924
  toneClass() {
2925
+ if (this.customAccent()) {
2926
+ return 'africanies-chip--custom';
2927
+ }
2910
2928
  switch (this.variant()) {
2911
2929
  case 'success':
2912
2930
  case 'export':
@@ -2917,6 +2935,16 @@ class ChipComponent {
2917
2935
  return 'bg-warning-subtle text-warning-dark dark:bg-warning/15 dark:text-warning';
2918
2936
  case 'danger':
2919
2937
  return 'bg-danger-subtle text-danger-dark dark:bg-danger/15 dark:text-danger';
2938
+ case 'info':
2939
+ return 'bg-info-subtle text-info dark:bg-info/20 dark:text-[#93c5fd]';
2940
+ case 'violet':
2941
+ return 'bg-violet-100 text-violet-800 dark:bg-violet-400/15 dark:text-violet-300';
2942
+ case 'teal':
2943
+ return 'bg-teal-100 text-teal-800 dark:bg-teal-400/15 dark:text-teal-300';
2944
+ case 'rose':
2945
+ return 'bg-rose-100 text-rose-800 dark:bg-rose-400/15 dark:text-rose-300';
2946
+ case 'cyan':
2947
+ return 'bg-cyan-100 text-cyan-800 dark:bg-cyan-400/15 dark:text-cyan-300';
2920
2948
  default:
2921
2949
  return 'bg-border/60 text-ink dark:bg-white/10 dark:text-neutral-200';
2922
2950
  }
@@ -2929,7 +2957,7 @@ class ChipComponent {
2929
2957
  this.removed.emit();
2930
2958
  }
2931
2959
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2932
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: ChipComponent, isStandalone: true, selector: "africanies-chip", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, removable: { classPropertyName: "removable", publicName: "removable", isSignal: true, isRequired: false, transformFunction: null }, removeLabel: { classPropertyName: "removeLabel", publicName: "removeLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { removed: "removed" }, host: { attributes: { "role": "status" }, properties: { "class": "hostClass()" } }, ngImport: i0, template: `
2960
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: ChipComponent, isStandalone: true, selector: "africanies-chip", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, removable: { classPropertyName: "removable", publicName: "removable", isSignal: true, isRequired: false, transformFunction: null }, removeLabel: { classPropertyName: "removeLabel", publicName: "removeLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { removed: "removed" }, host: { attributes: { "role": "status" }, properties: { "class": "hostClass()", "style.--africanies-chip-accent": "customAccent()" } }, ngImport: i0, template: `
2933
2961
  @if (icon(); as iconName) {
2934
2962
  <africanies-icon [name]="iconName" [size]="iconSize()" class="shrink-0" />
2935
2963
  }
@@ -2944,20 +2972,15 @@ class ChipComponent {
2944
2972
  <africanies-icon name="close" [size]="12" />
2945
2973
  </button>
2946
2974
  }
2947
- `, isInline: true, dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2975
+ `, isInline: true, styles: [":host.africanies-chip--custom{color:var(--africanies-chip-accent);background-color:color-mix(in srgb,var(--africanies-chip-accent) 14%,transparent);border-color:color-mix(in srgb,var(--africanies-chip-accent) 28%,transparent)}:host-context(.dark):host.africanies-chip--custom{color:color-mix(in srgb,var(--africanies-chip-accent) 68%,white);background-color:color-mix(in srgb,var(--africanies-chip-accent) 22%,transparent);border-color:color-mix(in srgb,var(--africanies-chip-accent) 40%,transparent)}\n"], dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2948
2976
  }
2949
2977
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChipComponent, decorators: [{
2950
2978
  type: Component,
2951
- args: [{
2952
- selector: 'africanies-chip',
2953
- standalone: true,
2954
- changeDetection: ChangeDetectionStrategy.OnPush,
2955
- imports: [AfricaniesIconComponent],
2956
- host: {
2979
+ args: [{ selector: 'africanies-chip', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AfricaniesIconComponent], host: {
2957
2980
  '[class]': 'hostClass()',
2981
+ '[style.--africanies-chip-accent]': 'customAccent()',
2958
2982
  role: 'status',
2959
- },
2960
- template: `
2983
+ }, template: `
2961
2984
  @if (icon(); as iconName) {
2962
2985
  <africanies-icon [name]="iconName" [size]="iconSize()" class="shrink-0" />
2963
2986
  }
@@ -2972,9 +2995,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2972
2995
  <africanies-icon name="close" [size]="12" />
2973
2996
  </button>
2974
2997
  }
2975
- `,
2976
- }]
2977
- }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], removable: [{ type: i0.Input, args: [{ isSignal: true, alias: "removable", required: false }] }], removeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "removeLabel", required: false }] }], removed: [{ type: i0.Output, args: ["removed"] }] } });
2998
+ `, styles: [":host.africanies-chip--custom{color:var(--africanies-chip-accent);background-color:color-mix(in srgb,var(--africanies-chip-accent) 14%,transparent);border-color:color-mix(in srgb,var(--africanies-chip-accent) 28%,transparent)}:host-context(.dark):host.africanies-chip--custom{color:color-mix(in srgb,var(--africanies-chip-accent) 68%,white);background-color:color-mix(in srgb,var(--africanies-chip-accent) 22%,transparent);border-color:color-mix(in srgb,var(--africanies-chip-accent) 40%,transparent)}\n"] }]
2999
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], removable: [{ type: i0.Input, args: [{ isSignal: true, alias: "removable", required: false }] }], removeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "removeLabel", required: false }] }], removed: [{ type: i0.Output, args: ["removed"] }] } });
2978
3000
 
2979
3001
  /**
2980
3002
  * Compact status / category labels (`africanies-chip`).
@@ -12162,7 +12184,7 @@ class SearchComboboxComponent {
12162
12184
  } @else if (hint(); as h) {
12163
12185
  <p [id]="hintId" [class]="hintClass">{{ h }}</p>
12164
12186
  }
12165
- `, isInline: true, dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "component", type: ChipComponent, selector: "africanies-chip", inputs: ["variant", "size", "icon", "removable", "removeLabel"], outputs: ["removed"] }, { kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "component", type: TooltipComponent, selector: "africanies-tooltip", inputs: ["text", "placement", "icon", "ariaLabel", "disabled"] }, { kind: "directive", type: TooltipTriggerDirective, selector: "[africaniesTooltipTrigger]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
12187
+ `, isInline: true, dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "component", type: ChipComponent, selector: "africanies-chip", inputs: ["variant", "color", "size", "icon", "removable", "removeLabel"], outputs: ["removed"] }, { kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "component", type: TooltipComponent, selector: "africanies-tooltip", inputs: ["text", "placement", "icon", "ariaLabel", "disabled"] }, { kind: "directive", type: TooltipTriggerDirective, selector: "[africaniesTooltipTrigger]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
12166
12188
  }
12167
12189
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SearchComboboxComponent, decorators: [{
12168
12190
  type: Component,
@@ -13286,6 +13308,10 @@ const DEFAULT_LINK_ACTIVE$2 = {
13286
13308
  /**
13287
13309
  * Compact segmented control for view modes and filters.
13288
13310
  *
13311
+ * Selected option uses a **solid brand pill** (white label) on a neutral track —
13312
+ * same pattern as the portal shipping-mode toggle. With exactly two options the
13313
+ * pill slides; with more options each selected chip fills in place.
13314
+ *
13289
13315
  * ## Router mode (URL is source of truth)
13290
13316
  *
13291
13317
  * Set `routerLink` (and optional `queryParams`) on items. The selected pill
@@ -13335,15 +13361,16 @@ class SegmentComponent {
13335
13361
  */
13336
13362
  keepScroll = input(true, { ...(ngDevMode ? { debugName: "keepScroll" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
13337
13363
  url = toSignal(this.router.events.pipe(filter((e) => e instanceof NavigationEnd), map(() => this.router.url), startWith(this.router.url)), { initialValue: this.router.url });
13338
- segmentChrome = computed(() => 'inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-body-sm transition-colors duration-150 no-underline', /* @ts-ignore */
13339
- ...(ngDevMode ? [{ debugName: "segmentChrome" }] : /* istanbul ignore next */ []));
13340
- selectedClass = computed(() => {
13341
- const colors = this.modeColor.classes();
13342
- return `${colors.soft} ${colors.text} shadow-sm font-semibold`;
13364
+ /** Two equal options portal-style sliding brand pill. */
13365
+ useSlidingThumb = computed(() => this.items().length === 2, /* @ts-ignore */
13366
+ ...(ngDevMode ? [{ debugName: "useSlidingThumb" }] : /* istanbul ignore next */ []));
13367
+ /** Index of the active option (0 / 1 for the sliding thumb). */
13368
+ activeIndex = computed(() => {
13369
+ const list = this.items();
13370
+ const idx = list.findIndex((item) => this.isItemActive(item));
13371
+ return idx < 0 ? 0 : idx;
13343
13372
  }, /* @ts-ignore */
13344
- ...(ngDevMode ? [{ debugName: "selectedClass" }] : /* istanbul ignore next */ []));
13345
- idleClass = computed(() => 'font-medium text-neutral-600 hover:text-ink dark:text-neutral-400 dark:hover:text-white', /* @ts-ignore */
13346
- ...(ngDevMode ? [{ debugName: "idleClass" }] : /* istanbul ignore next */ []));
13373
+ ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
13347
13374
  constructor() {
13348
13375
  effect(() => {
13349
13376
  this.url();
@@ -13368,6 +13395,56 @@ class SegmentComponent {
13368
13395
  }
13369
13396
  return this.activeId() === item.id;
13370
13397
  }
13398
+ /**
13399
+ * Sliding thumb fill. STN / SFN options keep their own brand color so the
13400
+ * pill matches mode identity; other pairs follow the active theme primary.
13401
+ */
13402
+ thumbFillClass() {
13403
+ const active = this.items()[this.activeIndex()];
13404
+ return this.brandFillFor(active?.id ?? null);
13405
+ }
13406
+ /**
13407
+ * Option chrome: sliding mode keeps labels transparent over the thumb;
13408
+ * multi-option mode paints a solid selected chip in place.
13409
+ *
13410
+ * @param item - Option being rendered.
13411
+ * @returns Class string for the control.
13412
+ */
13413
+ optionChrome(item) {
13414
+ const base = 'relative z-10 inline-flex min-w-0 items-center justify-center gap-1.5 rounded-md px-3 py-1.5 text-center text-body-sm no-underline transition-colors duration-150';
13415
+ const active = this.isItemActive(item);
13416
+ if (this.useSlidingThumb()) {
13417
+ return (base +
13418
+ ' w-full ' +
13419
+ (active
13420
+ ? 'font-semibold text-white'
13421
+ : 'font-medium text-ink hover:text-ink dark:text-neutral-300 dark:hover:text-white'));
13422
+ }
13423
+ if (active) {
13424
+ return `${base} font-semibold text-white shadow-sm ${this.brandFillFor(item.id)}`;
13425
+ }
13426
+ return (base +
13427
+ ' font-medium text-ink hover:bg-white/70 hover:text-ink dark:text-neutral-300 dark:hover:bg-white/10 dark:hover:text-white');
13428
+ }
13429
+ /**
13430
+ * Solid brand fill for a selected option / sliding thumb.
13431
+ *
13432
+ * Light + dark use the same SFN green / STN orange primaries so the control
13433
+ * stays mode-identifiable on both surfaces (portal primary, not soft tint).
13434
+ * Class strings are full literals so Tailwind keeps them.
13435
+ *
13436
+ * @param id - Option id (`stn` / `sfn` get fixed brands; else theme primary).
13437
+ * @returns Background utility classes.
13438
+ */
13439
+ brandFillFor(id) {
13440
+ if (id === 'stn') {
13441
+ return 'bg-import dark:bg-import';
13442
+ }
13443
+ if (id === 'sfn') {
13444
+ return 'bg-export dark:bg-export';
13445
+ }
13446
+ return this.modeColor.classes().activeFill;
13447
+ }
13371
13448
  selectLocal(item) {
13372
13449
  if (item.disabled || item.routerLink != null) {
13373
13450
  return;
@@ -13394,18 +13471,31 @@ class SegmentComponent {
13394
13471
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SegmentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13395
13472
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: SegmentComponent, isStandalone: true, selector: "africanies-segment", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, activeId: { classPropertyName: "activeId", publicName: "activeId", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, linkActiveOptions: { classPropertyName: "linkActiveOptions", publicName: "linkActiveOptions", isSignal: true, isRequired: false, transformFunction: null }, keepScroll: { classPropertyName: "keepScroll", publicName: "keepScroll", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeId: "activeIdChange" }, ngImport: i0, template: `
13396
13473
  <div
13397
- class="inline-flex max-w-full flex-wrap gap-1 rounded-lg bg-background-welcome p-1 dark:bg-ink-950"
13474
+ class="relative inline-grid max-w-full rounded-lg bg-[#f0f2f5] p-1 dark:bg-[#2a2c31]"
13398
13475
  role="group"
13399
13476
  [attr.aria-label]="ariaLabel()"
13477
+ [class.grid-cols-2]="useSlidingThumb()"
13478
+ [class.inline-flex]="!useSlidingThumb()"
13479
+ [class.flex-wrap]="!useSlidingThumb()"
13480
+ [class.gap-1]="!useSlidingThumb()"
13400
13481
  >
13482
+ @if (useSlidingThumb()) {
13483
+ <div
13484
+ class="africanies-segment-thumb pointer-events-none absolute top-1 bottom-1 left-1 rounded-md shadow-sm transition-transform duration-200 ease-out motion-reduce:transition-none"
13485
+ [class]="thumbFillClass()"
13486
+ [style.transform]="
13487
+ activeIndex() === 1
13488
+ ? 'translateX(calc(100% + 0.25rem))'
13489
+ : 'translateX(0)'
13490
+ "
13491
+ aria-hidden="true"
13492
+ ></div>
13493
+ }
13494
+
13401
13495
  @for (item of items(); track item.id) {
13402
13496
  @if (item.routerLink !== null && item.routerLink !== undefined) {
13403
13497
  <a
13404
- [class]="
13405
- segmentChrome() +
13406
- ' ' +
13407
- (isItemActive(item) ? selectedClass() : idleClass())
13408
- "
13498
+ [class]="optionChrome(item)"
13409
13499
  [class.cursor-pointer]="!item.disabled"
13410
13500
  [class.cursor-not-allowed]="item.disabled"
13411
13501
  [class.opacity-50]="item.disabled"
@@ -13422,11 +13512,7 @@ class SegmentComponent {
13422
13512
  } @else {
13423
13513
  <button
13424
13514
  type="button"
13425
- [class]="
13426
- segmentChrome() +
13427
- ' ' +
13428
- (isItemActive(item) ? selectedClass() : idleClass())
13429
- "
13515
+ [class]="optionChrome(item)"
13430
13516
  [disabled]="!!item.disabled"
13431
13517
  [class.cursor-pointer]="!item.disabled"
13432
13518
  [class.cursor-not-allowed]="item.disabled"
@@ -13441,29 +13527,37 @@ class SegmentComponent {
13441
13527
  }
13442
13528
  }
13443
13529
  </div>
13444
- `, isInline: true, dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13530
+ `, isInline: true, styles: [":host .africanies-segment-thumb{width:calc((100% - .5rem)/2)}\n"], dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13445
13531
  }
13446
13532
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SegmentComponent, decorators: [{
13447
13533
  type: Component,
13448
- args: [{
13449
- selector: 'africanies-segment',
13450
- standalone: true,
13451
- changeDetection: ChangeDetectionStrategy.OnPush,
13452
- imports: [AfricaniesIconComponent],
13453
- template: `
13534
+ args: [{ selector: 'africanies-segment', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AfricaniesIconComponent], template: `
13454
13535
  <div
13455
- class="inline-flex max-w-full flex-wrap gap-1 rounded-lg bg-background-welcome p-1 dark:bg-ink-950"
13536
+ class="relative inline-grid max-w-full rounded-lg bg-[#f0f2f5] p-1 dark:bg-[#2a2c31]"
13456
13537
  role="group"
13457
13538
  [attr.aria-label]="ariaLabel()"
13539
+ [class.grid-cols-2]="useSlidingThumb()"
13540
+ [class.inline-flex]="!useSlidingThumb()"
13541
+ [class.flex-wrap]="!useSlidingThumb()"
13542
+ [class.gap-1]="!useSlidingThumb()"
13458
13543
  >
13544
+ @if (useSlidingThumb()) {
13545
+ <div
13546
+ class="africanies-segment-thumb pointer-events-none absolute top-1 bottom-1 left-1 rounded-md shadow-sm transition-transform duration-200 ease-out motion-reduce:transition-none"
13547
+ [class]="thumbFillClass()"
13548
+ [style.transform]="
13549
+ activeIndex() === 1
13550
+ ? 'translateX(calc(100% + 0.25rem))'
13551
+ : 'translateX(0)'
13552
+ "
13553
+ aria-hidden="true"
13554
+ ></div>
13555
+ }
13556
+
13459
13557
  @for (item of items(); track item.id) {
13460
13558
  @if (item.routerLink !== null && item.routerLink !== undefined) {
13461
13559
  <a
13462
- [class]="
13463
- segmentChrome() +
13464
- ' ' +
13465
- (isItemActive(item) ? selectedClass() : idleClass())
13466
- "
13560
+ [class]="optionChrome(item)"
13467
13561
  [class.cursor-pointer]="!item.disabled"
13468
13562
  [class.cursor-not-allowed]="item.disabled"
13469
13563
  [class.opacity-50]="item.disabled"
@@ -13480,11 +13574,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
13480
13574
  } @else {
13481
13575
  <button
13482
13576
  type="button"
13483
- [class]="
13484
- segmentChrome() +
13485
- ' ' +
13486
- (isItemActive(item) ? selectedClass() : idleClass())
13487
- "
13577
+ [class]="optionChrome(item)"
13488
13578
  [disabled]="!!item.disabled"
13489
13579
  [class.cursor-pointer]="!item.disabled"
13490
13580
  [class.cursor-not-allowed]="item.disabled"
@@ -13499,10 +13589,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
13499
13589
  }
13500
13590
  }
13501
13591
  </div>
13502
- `,
13503
- }]
13592
+ `, styles: [":host .africanies-segment-thumb{width:calc((100% - .5rem)/2)}\n"] }]
13504
13593
  }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], 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 }] }], linkActiveOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "linkActiveOptions", required: false }] }], keepScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepScroll", required: false }] }] } });
13505
13594
 
13595
+ const PANEL_OPEN_STORAGE_KEY = 'africanies-shipping-mode-panel-open';
13596
+ function readStoredPanelOpen() {
13597
+ if (typeof localStorage === 'undefined') {
13598
+ return true;
13599
+ }
13600
+ try {
13601
+ const raw = localStorage.getItem(PANEL_OPEN_STORAGE_KEY);
13602
+ if (raw === null) {
13603
+ return true;
13604
+ }
13605
+ return raw !== '0' && raw !== 'false';
13606
+ }
13607
+ catch {
13608
+ return true;
13609
+ }
13610
+ }
13611
+ function writeStoredPanelOpen(open) {
13612
+ if (typeof localStorage === 'undefined') {
13613
+ return;
13614
+ }
13615
+ try {
13616
+ localStorage.setItem(PANEL_OPEN_STORAGE_KEY, open ? '1' : '0');
13617
+ }
13618
+ catch {
13619
+ // Private mode / quota — panel still toggles in-session.
13620
+ }
13621
+ }
13506
13622
  /**
13507
13623
  * Shared globe + plane glyph for STN / SFN. STN paints it rotated 180°.
13508
13624
  *
@@ -13517,12 +13633,13 @@ const SHIPPING_MODE_GLYPH = `M88.42,68.75a48.8,48.8,0,0,1-4.74,11.1h9.89a7.67,7.
13517
13633
  * import orange; **from Nigeria** uses export green. Both tiles keep equal
13518
13634
  * chrome so unselected does not look like floating text.
13519
13635
  *
13520
- * Project into `<africanies-side-nav>` with the `footer` attribute.
13636
+ * Project into `<africanies-side-nav>` with the `pre-nav` attribute (above the
13637
+ * nav list). `footer` still works for legacy placement under the list.
13521
13638
  *
13522
13639
  * @example
13523
13640
  * ```html
13524
13641
  * <africanies-side-nav [items]="nav" [(collapsed)]="collapsed">
13525
- * <africanies-shipping-mode-switch footer [collapsed]="collapsed()" />
13642
+ * <africanies-shipping-mode-switch pre-nav [collapsed]="collapsed()" />
13526
13643
  * </africanies-side-nav>
13527
13644
  * ```
13528
13645
  */
@@ -13543,13 +13660,32 @@ class ShippingModeSwitchComponent {
13543
13660
  * Icon-only stacked tiles when the side nav rail is collapsed.
13544
13661
  */
13545
13662
  collapsed = input(false, { ...(ngDevMode ? { debugName: "collapsed" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
13663
+ /**
13664
+ * When true, the mode tiles are visible. Persisted in `localStorage`.
13665
+ * Two-way bindable for hosts that need to force-open (e.g. onboarding).
13666
+ */
13667
+ panelOpen = model(readStoredPanelOpen(), /* @ts-ignore */
13668
+ ...(ngDevMode ? [{ debugName: "panelOpen" }] : /* istanbul ignore next */ []));
13546
13669
  /** Active mode from {@link ShippingModeService}. */
13547
13670
  mode = this.shipping.mode;
13548
- /** Host padding / divider tighter when the rail is collapsed. */
13671
+ modeChipLabel = computed(() => this.mode() === 'stn' ? 'STN' : 'SFN', /* @ts-ignore */
13672
+ ...(ngDevMode ? [{ debugName: "modeChipLabel" }] : /* istanbul ignore next */ []));
13673
+ modeChipVariant = computed(() => (this.mode() === 'stn' ? 'import' : 'export'), /* @ts-ignore */
13674
+ ...(ngDevMode ? [{ debugName: "modeChipVariant" }] : /* istanbul ignore next */ []));
13675
+ /** Host padding / divider — sits above the nav list (`border-b`). */
13549
13676
  hostClass = computed(() => this.collapsed()
13550
- ? 'block shrink-0 border-t border-border px-1.5 py-3 dark:border-white/10'
13551
- : 'block shrink-0 border-t border-border px-2 py-3 dark:border-white/10', /* @ts-ignore */
13677
+ ? 'block shrink-0 border-b border-border px-1.5 py-2 dark:border-white/10'
13678
+ : 'block shrink-0 border-b border-border px-2 py-2.5 dark:border-white/10', /* @ts-ignore */
13552
13679
  ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
13680
+ constructor() {
13681
+ effect(() => {
13682
+ writeStoredPanelOpen(this.panelOpen());
13683
+ });
13684
+ }
13685
+ /** Show or hide the mode tiles. */
13686
+ togglePanel() {
13687
+ this.panelOpen.update((open) => !open);
13688
+ }
13553
13689
  /**
13554
13690
  * Requests a mode change through {@link ShippingModeService.requestModeChange}
13555
13691
  * so feature guards (e.g. Create Shipment confirm) can run first.
@@ -13573,7 +13709,7 @@ class ShippingModeSwitchComponent {
13573
13709
  const compact = this.collapsed();
13574
13710
  const base = 'flex w-full cursor-pointer flex-col items-center justify-center gap-1 rounded-lg border text-center text-caption font-medium transition-colors ' +
13575
13711
  'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus ' +
13576
- (compact ? 'min-h-9 px-1 py-1.5' : 'min-h-[4.5rem] px-1.5 py-2');
13712
+ (compact ? 'size-9 min-h-0 px-0 py-0' : 'min-h-[4.5rem] px-1.5 py-2');
13577
13713
  const idle = 'border-control bg-surface text-ink hover:bg-background-hover dark:border-white/15 dark:bg-ink-950 dark:text-white dark:hover:bg-white/10';
13578
13714
  if (option === 'stn') {
13579
13715
  return selected
@@ -13581,73 +13717,141 @@ class ShippingModeSwitchComponent {
13581
13717
  : `${base} ${idle}`;
13582
13718
  }
13583
13719
  return selected
13584
- ? `${base} border-export-strong bg-export-strong text-white dark:border-export dark:bg-export`
13720
+ ? `${base} border-export bg-export text-white dark:border-export dark:bg-export`
13585
13721
  : `${base} ${idle}`;
13586
13722
  }
13587
13723
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ShippingModeSwitchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13588
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: ShippingModeSwitchComponent, isStandalone: true, selector: "africanies-shipping-mode-switch", inputs: { collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClass()" } }, ngImport: i0, template: `
13589
- @if (!collapsed()) {
13590
- <p
13591
- class="m-0 mb-2 px-1 text-caption font-semibold uppercase tracking-wide text-neutral-600 dark:text-neutral-400"
13592
- >
13593
- Shipping mode
13594
- </p>
13595
- }
13596
- <div
13597
- class="grid gap-1.5"
13598
- [class.grid-cols-1]="collapsed()"
13599
- [class.grid-cols-2]="!collapsed()"
13600
- role="radiogroup"
13601
- aria-label="Shipping mode"
13602
- >
13603
- <button
13604
- type="button"
13605
- role="radio"
13606
- [attr.aria-checked]="mode() === 'stn'"
13607
- aria-label="Shipping to Nigeria"
13608
- [class]="cardClass('stn')"
13609
- (click)="select('stn')"
13724
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: ShippingModeSwitchComponent, isStandalone: true, selector: "africanies-shipping-mode-switch", inputs: { collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, panelOpen: { classPropertyName: "panelOpen", publicName: "panelOpen", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { panelOpen: "panelOpenChange" }, host: { properties: { "class": "hostClass()" } }, ngImport: i0, template: `
13725
+ @if (collapsed()) {
13726
+ <!-- Narrow rail: icon tiles only — chip + chevron reads as cramped. -->
13727
+ <div
13728
+ class="grid grid-cols-1 gap-1.5"
13729
+ role="radiogroup"
13730
+ aria-label="Shipping mode"
13610
13731
  >
13611
- <svg
13612
- viewBox="0 0 122.88 107.54"
13613
- class="h-6 w-6 shrink-0 rotate-180"
13614
- aria-hidden="true"
13615
- focusable="false"
13732
+ <button
13733
+ type="button"
13734
+ role="radio"
13735
+ [attr.aria-checked]="mode() === 'stn'"
13736
+ aria-label="Shipping to Nigeria"
13737
+ title="Shipping to Nigeria"
13738
+ [class]="cardClass('stn')"
13739
+ (click)="select('stn')"
13616
13740
  >
13617
- <path [attr.d]="glyphPath()" fill="currentColor" />
13618
- </svg>
13619
- @if (!collapsed()) {
13620
- <span class="flex flex-col leading-tight">
13621
- <span>Shipping</span>
13622
- <span>to Nigeria</span>
13623
- </span>
13624
- }
13625
- </button>
13626
- <button
13627
- type="button"
13628
- role="radio"
13629
- [attr.aria-checked]="mode() === 'sfn'"
13630
- aria-label="Shipping from Nigeria"
13631
- [class]="cardClass('sfn')"
13632
- (click)="select('sfn')"
13633
- >
13634
- <svg
13635
- viewBox="0 0 122.88 107.54"
13636
- class="h-6 w-6 shrink-0"
13637
- aria-hidden="true"
13638
- focusable="false"
13741
+ <svg
13742
+ viewBox="0 0 122.88 107.54"
13743
+ class="h-5 w-5 shrink-0 rotate-180"
13744
+ aria-hidden="true"
13745
+ focusable="false"
13746
+ >
13747
+ <path [attr.d]="glyphPath()" fill="currentColor" />
13748
+ </svg>
13749
+ </button>
13750
+ <button
13751
+ type="button"
13752
+ role="radio"
13753
+ [attr.aria-checked]="mode() === 'sfn'"
13754
+ aria-label="Shipping from Nigeria"
13755
+ title="Shipping from Nigeria"
13756
+ [class]="cardClass('sfn')"
13757
+ (click)="select('sfn')"
13639
13758
  >
13640
- <path [attr.d]="glyphPath()" fill="currentColor" />
13641
- </svg>
13642
- @if (!collapsed()) {
13643
- <span class="flex flex-col leading-tight">
13644
- <span>Shipping</span>
13645
- <span>from Nigeria</span>
13646
- </span>
13647
- }
13648
- </button>
13649
- </div>
13650
- `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
13759
+ <svg
13760
+ viewBox="0 0 122.88 107.54"
13761
+ class="h-5 w-5 shrink-0"
13762
+ aria-hidden="true"
13763
+ focusable="false"
13764
+ >
13765
+ <path [attr.d]="glyphPath()" fill="currentColor" />
13766
+ </svg>
13767
+ </button>
13768
+ </div>
13769
+ } @else {
13770
+ <div class="mb-2 flex items-center gap-1.5">
13771
+ <p
13772
+ class="m-0 shrink-0 px-1 text-caption font-semibold uppercase tracking-wide text-neutral-600 dark:text-neutral-400"
13773
+ >
13774
+ Shipping mode
13775
+ </p>
13776
+ <africanies-chip [variant]="modeChipVariant()" size="sm">
13777
+ {{ modeChipLabel() }}
13778
+ </africanies-chip>
13779
+ <span class="min-w-0 flex-1" aria-hidden="true"></span>
13780
+ <button
13781
+ type="button"
13782
+ class="inline-flex size-7 shrink-0 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
13783
+ [attr.aria-label]="
13784
+ panelOpen() ? 'Hide shipping modes' : 'Show shipping modes'
13785
+ "
13786
+ [attr.aria-expanded]="panelOpen()"
13787
+ (click)="togglePanel()"
13788
+ >
13789
+ <africanies-icon
13790
+ [name]="panelOpen() ? 'chevron-up' : 'chevron-down'"
13791
+ [size]="14"
13792
+ />
13793
+ </button>
13794
+ </div>
13795
+
13796
+ <div
13797
+ class="grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none"
13798
+ [style.grid-template-rows]="panelOpen() ? '1fr' : '0fr'"
13799
+ [attr.aria-hidden]="!panelOpen()"
13800
+ [class.pointer-events-none]="!panelOpen()"
13801
+ >
13802
+ <div class="min-h-0 overflow-hidden">
13803
+ <div
13804
+ class="grid grid-cols-2 gap-1.5"
13805
+ role="radiogroup"
13806
+ aria-label="Shipping mode"
13807
+ >
13808
+ <button
13809
+ type="button"
13810
+ role="radio"
13811
+ [attr.aria-checked]="mode() === 'stn'"
13812
+ aria-label="Shipping to Nigeria"
13813
+ [class]="cardClass('stn')"
13814
+ (click)="select('stn')"
13815
+ >
13816
+ <svg
13817
+ viewBox="0 0 122.88 107.54"
13818
+ class="h-6 w-6 shrink-0 rotate-180"
13819
+ aria-hidden="true"
13820
+ focusable="false"
13821
+ >
13822
+ <path [attr.d]="glyphPath()" fill="currentColor" />
13823
+ </svg>
13824
+ <span class="flex flex-col leading-tight">
13825
+ <span>Shipping</span>
13826
+ <span>to Nigeria</span>
13827
+ </span>
13828
+ </button>
13829
+ <button
13830
+ type="button"
13831
+ role="radio"
13832
+ [attr.aria-checked]="mode() === 'sfn'"
13833
+ aria-label="Shipping from Nigeria"
13834
+ [class]="cardClass('sfn')"
13835
+ (click)="select('sfn')"
13836
+ >
13837
+ <svg
13838
+ viewBox="0 0 122.88 107.54"
13839
+ class="h-6 w-6 shrink-0"
13840
+ aria-hidden="true"
13841
+ focusable="false"
13842
+ >
13843
+ <path [attr.d]="glyphPath()" fill="currentColor" />
13844
+ </svg>
13845
+ <span class="flex flex-col leading-tight">
13846
+ <span>Shipping</span>
13847
+ <span>from Nigeria</span>
13848
+ </span>
13849
+ </button>
13850
+ </div>
13851
+ </div>
13852
+ </div>
13853
+ }
13854
+ `, isInline: true, dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "component", type: ChipComponent, selector: "africanies-chip", inputs: ["variant", "color", "size", "icon", "removable", "removeLabel"], outputs: ["removed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13651
13855
  }
13652
13856
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ShippingModeSwitchComponent, decorators: [{
13653
13857
  type: Component,
@@ -13655,74 +13859,143 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
13655
13859
  selector: 'africanies-shipping-mode-switch',
13656
13860
  standalone: true,
13657
13861
  changeDetection: ChangeDetectionStrategy.OnPush,
13862
+ imports: [AfricaniesIconComponent, ChipComponent],
13658
13863
  host: {
13659
13864
  '[class]': 'hostClass()',
13660
13865
  },
13661
13866
  template: `
13662
- @if (!collapsed()) {
13663
- <p
13664
- class="m-0 mb-2 px-1 text-caption font-semibold uppercase tracking-wide text-neutral-600 dark:text-neutral-400"
13665
- >
13666
- Shipping mode
13667
- </p>
13668
- }
13669
- <div
13670
- class="grid gap-1.5"
13671
- [class.grid-cols-1]="collapsed()"
13672
- [class.grid-cols-2]="!collapsed()"
13673
- role="radiogroup"
13674
- aria-label="Shipping mode"
13675
- >
13676
- <button
13677
- type="button"
13678
- role="radio"
13679
- [attr.aria-checked]="mode() === 'stn'"
13680
- aria-label="Shipping to Nigeria"
13681
- [class]="cardClass('stn')"
13682
- (click)="select('stn')"
13867
+ @if (collapsed()) {
13868
+ <!-- Narrow rail: icon tiles only — chip + chevron reads as cramped. -->
13869
+ <div
13870
+ class="grid grid-cols-1 gap-1.5"
13871
+ role="radiogroup"
13872
+ aria-label="Shipping mode"
13683
13873
  >
13684
- <svg
13685
- viewBox="0 0 122.88 107.54"
13686
- class="h-6 w-6 shrink-0 rotate-180"
13687
- aria-hidden="true"
13688
- focusable="false"
13874
+ <button
13875
+ type="button"
13876
+ role="radio"
13877
+ [attr.aria-checked]="mode() === 'stn'"
13878
+ aria-label="Shipping to Nigeria"
13879
+ title="Shipping to Nigeria"
13880
+ [class]="cardClass('stn')"
13881
+ (click)="select('stn')"
13689
13882
  >
13690
- <path [attr.d]="glyphPath()" fill="currentColor" />
13691
- </svg>
13692
- @if (!collapsed()) {
13693
- <span class="flex flex-col leading-tight">
13694
- <span>Shipping</span>
13695
- <span>to Nigeria</span>
13696
- </span>
13697
- }
13698
- </button>
13699
- <button
13700
- type="button"
13701
- role="radio"
13702
- [attr.aria-checked]="mode() === 'sfn'"
13703
- aria-label="Shipping from Nigeria"
13704
- [class]="cardClass('sfn')"
13705
- (click)="select('sfn')"
13706
- >
13707
- <svg
13708
- viewBox="0 0 122.88 107.54"
13709
- class="h-6 w-6 shrink-0"
13710
- aria-hidden="true"
13711
- focusable="false"
13883
+ <svg
13884
+ viewBox="0 0 122.88 107.54"
13885
+ class="h-5 w-5 shrink-0 rotate-180"
13886
+ aria-hidden="true"
13887
+ focusable="false"
13888
+ >
13889
+ <path [attr.d]="glyphPath()" fill="currentColor" />
13890
+ </svg>
13891
+ </button>
13892
+ <button
13893
+ type="button"
13894
+ role="radio"
13895
+ [attr.aria-checked]="mode() === 'sfn'"
13896
+ aria-label="Shipping from Nigeria"
13897
+ title="Shipping from Nigeria"
13898
+ [class]="cardClass('sfn')"
13899
+ (click)="select('sfn')"
13712
13900
  >
13713
- <path [attr.d]="glyphPath()" fill="currentColor" />
13714
- </svg>
13715
- @if (!collapsed()) {
13716
- <span class="flex flex-col leading-tight">
13717
- <span>Shipping</span>
13718
- <span>from Nigeria</span>
13719
- </span>
13720
- }
13721
- </button>
13722
- </div>
13901
+ <svg
13902
+ viewBox="0 0 122.88 107.54"
13903
+ class="h-5 w-5 shrink-0"
13904
+ aria-hidden="true"
13905
+ focusable="false"
13906
+ >
13907
+ <path [attr.d]="glyphPath()" fill="currentColor" />
13908
+ </svg>
13909
+ </button>
13910
+ </div>
13911
+ } @else {
13912
+ <div class="mb-2 flex items-center gap-1.5">
13913
+ <p
13914
+ class="m-0 shrink-0 px-1 text-caption font-semibold uppercase tracking-wide text-neutral-600 dark:text-neutral-400"
13915
+ >
13916
+ Shipping mode
13917
+ </p>
13918
+ <africanies-chip [variant]="modeChipVariant()" size="sm">
13919
+ {{ modeChipLabel() }}
13920
+ </africanies-chip>
13921
+ <span class="min-w-0 flex-1" aria-hidden="true"></span>
13922
+ <button
13923
+ type="button"
13924
+ class="inline-flex size-7 shrink-0 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
13925
+ [attr.aria-label]="
13926
+ panelOpen() ? 'Hide shipping modes' : 'Show shipping modes'
13927
+ "
13928
+ [attr.aria-expanded]="panelOpen()"
13929
+ (click)="togglePanel()"
13930
+ >
13931
+ <africanies-icon
13932
+ [name]="panelOpen() ? 'chevron-up' : 'chevron-down'"
13933
+ [size]="14"
13934
+ />
13935
+ </button>
13936
+ </div>
13937
+
13938
+ <div
13939
+ class="grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none"
13940
+ [style.grid-template-rows]="panelOpen() ? '1fr' : '0fr'"
13941
+ [attr.aria-hidden]="!panelOpen()"
13942
+ [class.pointer-events-none]="!panelOpen()"
13943
+ >
13944
+ <div class="min-h-0 overflow-hidden">
13945
+ <div
13946
+ class="grid grid-cols-2 gap-1.5"
13947
+ role="radiogroup"
13948
+ aria-label="Shipping mode"
13949
+ >
13950
+ <button
13951
+ type="button"
13952
+ role="radio"
13953
+ [attr.aria-checked]="mode() === 'stn'"
13954
+ aria-label="Shipping to Nigeria"
13955
+ [class]="cardClass('stn')"
13956
+ (click)="select('stn')"
13957
+ >
13958
+ <svg
13959
+ viewBox="0 0 122.88 107.54"
13960
+ class="h-6 w-6 shrink-0 rotate-180"
13961
+ aria-hidden="true"
13962
+ focusable="false"
13963
+ >
13964
+ <path [attr.d]="glyphPath()" fill="currentColor" />
13965
+ </svg>
13966
+ <span class="flex flex-col leading-tight">
13967
+ <span>Shipping</span>
13968
+ <span>to Nigeria</span>
13969
+ </span>
13970
+ </button>
13971
+ <button
13972
+ type="button"
13973
+ role="radio"
13974
+ [attr.aria-checked]="mode() === 'sfn'"
13975
+ aria-label="Shipping from Nigeria"
13976
+ [class]="cardClass('sfn')"
13977
+ (click)="select('sfn')"
13978
+ >
13979
+ <svg
13980
+ viewBox="0 0 122.88 107.54"
13981
+ class="h-6 w-6 shrink-0"
13982
+ aria-hidden="true"
13983
+ focusable="false"
13984
+ >
13985
+ <path [attr.d]="glyphPath()" fill="currentColor" />
13986
+ </svg>
13987
+ <span class="flex flex-col leading-tight">
13988
+ <span>Shipping</span>
13989
+ <span>from Nigeria</span>
13990
+ </span>
13991
+ </button>
13992
+ </div>
13993
+ </div>
13994
+ </div>
13995
+ }
13723
13996
  `,
13724
13997
  }]
13725
- }], propDecorators: { collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }] } });
13998
+ }], ctorParameters: () => [], propDecorators: { collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], panelOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelOpen", required: false }] }, { type: i0.Output, args: ["panelOpenChange"] }] } });
13726
13999
 
13727
14000
  /**
13728
14001
  * Side-nav shipping mode switcher (`africanies-shipping-mode-switch`).
@@ -13869,7 +14142,9 @@ const DEFAULT_LINK_ACTIVE$1 = {
13869
14142
  * **Expanded rail:** branches start open by default (`expandBranchesByDefault`).
13870
14143
  * Clicking a parent only opens/closes the branch (no route change). When any
13871
14144
  * branch is open, a sticky collapse-all control sits above the list (top right).
13872
- * The rail expand/collapse control stays pinned in the logo header.
14145
+ * The rail expand/collapse control stays pinned in the logo header. Project
14146
+ * chrome above the scrollable list with `pre-nav` (e.g. shipping modes) or
14147
+ * below with `footer`.
13873
14148
  * **Collapsed rail:** clicking a parent expands the rail, opens the
13874
14149
  * branch, and activates the first enabled child (router or `activeId`).
13875
14150
  *
@@ -14185,13 +14460,13 @@ class SideNavComponent {
14185
14460
  return null;
14186
14461
  }
14187
14462
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SideNavComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14188
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: SideNavComponent, isStandalone: true, selector: "africanies-side-nav", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, activeId: { classPropertyName: "activeId", publicName: "activeId", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, expandParents: { classPropertyName: "expandParents", publicName: "expandParents", isSignal: true, isRequired: false, transformFunction: null }, expandBranchesByDefault: { classPropertyName: "expandBranchesByDefault", publicName: "expandBranchesByDefault", isSignal: true, isRequired: false, transformFunction: null }, linkActiveOptions: { classPropertyName: "linkActiveOptions", publicName: "linkActiveOptions", isSignal: true, isRequired: false, transformFunction: null }, showLogo: { classPropertyName: "showLogo", publicName: "showLogo", isSignal: true, isRequired: false, transformFunction: null }, logoSrc: { classPropertyName: "logoSrc", publicName: "logoSrc", isSignal: true, isRequired: false, transformFunction: null }, logoMiniSrc: { classPropertyName: "logoMiniSrc", publicName: "logoMiniSrc", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsed: "collapsedChange", activeId: "activeIdChange" }, host: { classAttribute: "relative z-20 block h-full min-h-0" }, ngImport: i0, template: " <aside\n class=\"relative flex h-full min-h-0 flex-col overflow-visible border-r border-border bg-surface transition-[width] duration-200 ease-out dark:border-white/10 dark:bg-ink-950\"\n [style.width]=\"collapsed() ? '3.75rem' : '15rem'\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n <div\n class=\"pointer-events-none absolute inset-y-0 left-0 w-[3px] transition-colors\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n\n <div\n class=\"sticky top-0 z-20 flex shrink-0 items-center gap-2 border-b border-border bg-surface py-3 dark:border-white/10 dark:bg-ink-950\"\n [class.flex-col]=\"collapsed()\"\n [class.px-3]=\"collapsed()\"\n [class.pl-4]=\"!collapsed()\"\n [class.pr-2]=\"!collapsed()\"\n >\n @if (collapsed()) {\n @if (showLogo()) {\n <img\n [src]=\"resolvedMiniLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"size-8 shrink-0 object-contain\"\n />\n }\n } @else {\n @if (showLogo()) {\n <div class=\"min-w-0 flex-1\">\n <img\n [src]=\"resolvedFullLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"h-9 max-w-full object-contain object-left\"\n />\n </div>\n } @else {\n <div\n class=\"min-w-0 flex-1 truncate text-body-sm font-semibold text-ink dark:text-white\"\n >\n <ng-content select=\"[brand]\" />\n </div>\n }\n }\n <button\n type=\"button\"\n class=\"inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white\"\n data-onboarding=\"nav-collapse\"\n [attr.aria-label]=\"\n collapsed() ? 'Expand navigation' : 'Collapse navigation'\n \"\n [attr.aria-expanded]=\"!collapsed()\"\n (click)=\"toggleCollapsed()\"\n >\n <africanies-icon\n [name]=\"collapsed() ? 'angle-double-right' : 'angle-double-left'\"\n [size]=\"16\"\n />\n </button>\n </div>\n\n <nav\n class=\"africanies-overlay-scroll relative min-h-0 flex-1 overflow-y-auto overflow-x-visible px-3 py-3\"\n >\n @if (!collapsed() && expandParents() && hasOpenBranches()) {\n <!-- Zero-height sticky host: keeps the control pinned without reserving a row. -->\n <div class=\"sticky top-0 z-10 h-0 overflow-visible\">\n <button\n type=\"button\"\n class=\"absolute right-0 top-0 inline-flex size-7 cursor-pointer items-center justify-center rounded-md bg-surface text-neutral-600 shadow-sm ring-1 ring-border-strong transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:bg-ink-950 dark:text-neutral-400 dark:ring-white/15 dark:hover:bg-white/10 dark:hover:text-white\"\n data-onboarding=\"nav-branches-collapse\"\n aria-label=\"Collapse all\"\n (click)=\"collapseAllBranches()\"\n >\n <africanies-icon name=\"compress\" [size]=\"14\" />\n </button>\n </div>\n }\n <ul class=\"m-0 flex list-none flex-col gap-0.5 p-0\">\n @for (item of items(); track item.id) {\n <li\n class=\"relative overflow-visible last:mb-0\"\n [class.mb-3]=\"!collapsed() && !item.children?.length\"\n (mouseenter)=\"onItemEnter(item.id)\"\n (mouseleave)=\"onItemLeave(item.id)\"\n >\n @if (item.routerLink !== null && item.routerLink !== undefined) {\n <a\n [routerLink]=\"item.disabled ? null : item.routerLink\"\n [queryParams]=\"item.queryParams\"\n [fragment]=\"item.fragment\"\n [class]=\"rowClass(item)\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n [attr.aria-disabled]=\"item.disabled ? true : null\"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"rowClass(item)\"\n [disabled]=\"!!item.disabled\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </button>\n }\n\n @if (!collapsed() && item.children?.length) {\n <div\n class=\"grid transition-[grid-template-rows] duration-200 ease-out\"\n [style.grid-template-rows]=\"\n isBranchOpen(item.id) ? '1fr' : '0fr'\n \"\n [attr.aria-hidden]=\"!isBranchOpen(item.id)\"\n [class.pointer-events-none]=\"!isBranchOpen(item.id)\"\n >\n <ul\n class=\"relative m-0 mt-0.5 min-h-0 list-none space-y-0.5 overflow-hidden py-1 pl-4 before:absolute before:bottom-1 before:left-[1.35rem] before:top-0 before:w-px before:bg-border dark:before:bg-white/15\"\n >\n @for (child of item.children; track child.id) {\n <li\n class=\"transition-opacity duration-200 ease-out\"\n [class.opacity-0]=\"!isBranchOpen(item.id)\"\n [class.opacity-100]=\"isBranchOpen(item.id)\"\n >\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"child.disabled ? null : child.routerLink\"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n [class]=\"childRowClass(child)\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"childRowClass(child)\"\n [disabled]=\"!!child.disabled\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n </div>\n }\n\n @if (collapsed() && hoverId() === item.id) {\n <div\n class=\"absolute left-full top-0 z-50 ml-2 min-w-[11rem] overflow-hidden rounded-md border border-border bg-ink text-white shadow-lg dark:border-white/15\"\n role=\"tooltip\"\n >\n <div\n class=\"absolute inset-y-0 left-0 w-1\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n <div class=\"border-b border-white/10 px-3 py-2 pl-4\">\n <p class=\"m-0 text-body-sm font-semibold\">{{ item.label }}</p>\n </div>\n @if (item.children?.length) {\n <ul class=\"m-0 list-none space-y-0.5 p-1.5\">\n @for (child of item.children; track child.id) {\n <li>\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"\n child.disabled ? null : child.routerLink\n \"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n class=\"relative flex items-center gap-2 rounded px-2.5 py-1.5 text-body-sm text-white/85 no-underline transition-colors hover:bg-white/10 hover:text-white\"\n [class.opacity-50]=\"child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"relative flex w-full items-center gap-2 rounded px-2.5 py-1.5 text-left text-body-sm text-white/85 transition-colors hover:bg-white/10 hover:text-white\"\n [disabled]=\"!!child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n }\n </div>\n }\n </li>\n }\n </ul>\n </nav>\n\n <ng-content select=\"[footer]\" />\n </aside>\n\n <ng-template #rowInner let-item>\n <span class=\"inline-flex size-5 shrink-0 items-center justify-center\">\n @if (item.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"16\" />\n } @else {\n <span\n class=\"size-1.5 rounded-full bg-current opacity-70\"\n aria-hidden=\"true\"\n ></span>\n }\n </span>\n @if (!collapsed()) {\n <span class=\"min-w-0 flex-1 truncate text-left\">{{ item.label }}</span>\n @if (item.children?.length) {\n <africanies-icon\n name=\"chevron-down\"\n [size]=\"14\"\n class=\"shrink-0 opacity-70 transition-transform duration-200 ease-out\"\n [class.rotate-180]=\"isBranchOpen(item.id)\"\n />\n }\n }\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: item.badge,\n compact: collapsed(),\n }\"\n />\n </ng-template>\n\n <ng-template #navBadge let-visible=\"visible\" let-compact=\"compact\">\n @if (visible) {\n <span\n class=\"pointer-events-none absolute z-10 overflow-visible\"\n [class]=\"compact ? 'right-0.5 top-0.5 h-2 w-2' : 'right-1.5 top-1 h-2 w-2'\"\n aria-hidden=\"true\"\n >\n <span\n class=\"africanies-side-nav-badge-ping absolute left-1/2 top-1/2 size-2 rounded-full bg-danger\"\n ></span>\n <span\n class=\"absolute left-1/2 top-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-danger ring-2 ring-white dark:ring-ink\"\n ></span>\n </span>\n }\n </ng-template>", styles: ["@keyframes africanies-side-nav-badge-pulse{0%{transform:translate(-50%,-50%) scale(.85);opacity:.9}70%{transform:translate(-50%,-50%) scale(2.4);opacity:0}to{transform:translate(-50%,-50%) scale(2.4);opacity:0}}.africanies-side-nav-badge-ping{animation:africanies-side-nav-badge-pulse 1.4s cubic-bezier(0,0,.2,1) infinite}@media(prefers-reduced-motion:reduce){.africanies-side-nav-badge-ping{animation:none;opacity:.5}}\n"], dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14463
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: SideNavComponent, isStandalone: true, selector: "africanies-side-nav", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, activeId: { classPropertyName: "activeId", publicName: "activeId", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, expandParents: { classPropertyName: "expandParents", publicName: "expandParents", isSignal: true, isRequired: false, transformFunction: null }, expandBranchesByDefault: { classPropertyName: "expandBranchesByDefault", publicName: "expandBranchesByDefault", isSignal: true, isRequired: false, transformFunction: null }, linkActiveOptions: { classPropertyName: "linkActiveOptions", publicName: "linkActiveOptions", isSignal: true, isRequired: false, transformFunction: null }, showLogo: { classPropertyName: "showLogo", publicName: "showLogo", isSignal: true, isRequired: false, transformFunction: null }, logoSrc: { classPropertyName: "logoSrc", publicName: "logoSrc", isSignal: true, isRequired: false, transformFunction: null }, logoMiniSrc: { classPropertyName: "logoMiniSrc", publicName: "logoMiniSrc", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsed: "collapsedChange", activeId: "activeIdChange" }, host: { classAttribute: "relative z-20 block h-full min-h-0" }, ngImport: i0, template: " <aside\n class=\"relative flex h-full min-h-0 flex-col overflow-visible border-r border-border bg-surface transition-[width] duration-200 ease-out dark:border-white/10 dark:bg-ink-950\"\n [style.width]=\"collapsed() ? '3.75rem' : '15rem'\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n <div\n class=\"pointer-events-none absolute inset-y-0 left-0 w-[3px] transition-colors\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n\n <div\n class=\"sticky top-0 z-20 flex shrink-0 items-center gap-2 border-b border-border bg-surface py-3 dark:border-white/10 dark:bg-ink-950\"\n [class.flex-col]=\"collapsed()\"\n [class.px-3]=\"collapsed()\"\n [class.pl-4]=\"!collapsed()\"\n [class.pr-2]=\"!collapsed()\"\n >\n @if (collapsed()) {\n @if (showLogo()) {\n <img\n [src]=\"resolvedMiniLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"size-8 shrink-0 object-contain\"\n />\n }\n } @else {\n @if (showLogo()) {\n <div class=\"min-w-0 flex-1\">\n <img\n [src]=\"resolvedFullLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"h-9 max-w-full object-contain object-left\"\n />\n </div>\n } @else {\n <div\n class=\"min-w-0 flex-1 truncate text-body-sm font-semibold text-ink dark:text-white\"\n >\n <ng-content select=\"[brand]\" />\n </div>\n }\n }\n <button\n type=\"button\"\n class=\"inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white\"\n data-onboarding=\"nav-collapse\"\n [attr.aria-label]=\"\n collapsed() ? 'Expand navigation' : 'Collapse navigation'\n \"\n [attr.aria-expanded]=\"!collapsed()\"\n (click)=\"toggleCollapsed()\"\n >\n <africanies-icon\n [name]=\"collapsed() ? 'angle-double-right' : 'angle-double-left'\"\n [size]=\"16\"\n />\n </button>\n </div>\n\n <ng-content select=\"[pre-nav]\" />\n\n <nav\n class=\"africanies-overlay-scroll relative min-h-0 flex-1 overflow-y-auto overflow-x-visible px-3 py-3\"\n >\n @if (!collapsed() && expandParents() && hasOpenBranches()) {\n <!-- Zero-height sticky host: keeps the control pinned without reserving a row. -->\n <div class=\"sticky top-0 z-20 h-0 overflow-visible\">\n <button\n type=\"button\"\n class=\"absolute -right-2 -top-1 inline-flex size-7 cursor-pointer items-center justify-center rounded-md bg-surface text-neutral-600 shadow-sm ring-1 ring-border-strong transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:bg-ink-950 dark:text-neutral-400 dark:ring-white/15 dark:hover:bg-ink-surface dark:hover:text-white\"\n data-onboarding=\"nav-branches-collapse\"\n aria-label=\"Collapse all\"\n (click)=\"collapseAllBranches()\"\n >\n <africanies-icon name=\"compress\" [size]=\"14\" />\n </button>\n </div>\n }\n <ul class=\"m-0 flex list-none flex-col gap-0.5 p-0\">\n @for (item of items(); track item.id) {\n <li\n class=\"relative overflow-visible last:mb-0\"\n [class.mb-3]=\"!collapsed() && !item.children?.length\"\n (mouseenter)=\"onItemEnter(item.id)\"\n (mouseleave)=\"onItemLeave(item.id)\"\n >\n @if (item.routerLink !== null && item.routerLink !== undefined) {\n <a\n [routerLink]=\"item.disabled ? null : item.routerLink\"\n [queryParams]=\"item.queryParams\"\n [fragment]=\"item.fragment\"\n [class]=\"rowClass(item)\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n [attr.aria-disabled]=\"item.disabled ? true : null\"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"rowClass(item)\"\n [disabled]=\"!!item.disabled\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </button>\n }\n\n @if (!collapsed() && item.children?.length) {\n <div\n class=\"grid transition-[grid-template-rows] duration-200 ease-out\"\n [style.grid-template-rows]=\"\n isBranchOpen(item.id) ? '1fr' : '0fr'\n \"\n [attr.aria-hidden]=\"!isBranchOpen(item.id)\"\n [class.pointer-events-none]=\"!isBranchOpen(item.id)\"\n >\n <ul\n class=\"relative m-0 mt-0.5 min-h-0 list-none space-y-0.5 overflow-hidden py-1 pl-4 before:absolute before:bottom-1 before:left-[1.35rem] before:top-0 before:w-px before:bg-border dark:before:bg-white/15\"\n >\n @for (child of item.children; track child.id) {\n <li\n class=\"transition-opacity duration-200 ease-out\"\n [class.opacity-0]=\"!isBranchOpen(item.id)\"\n [class.opacity-100]=\"isBranchOpen(item.id)\"\n >\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"child.disabled ? null : child.routerLink\"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n [class]=\"childRowClass(child)\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"childRowClass(child)\"\n [disabled]=\"!!child.disabled\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n </div>\n }\n\n @if (collapsed() && hoverId() === item.id) {\n <div\n class=\"absolute left-full top-0 z-50 ml-2 min-w-[11rem] overflow-hidden rounded-md border border-border bg-ink text-white shadow-lg dark:border-white/15\"\n role=\"tooltip\"\n >\n <div\n class=\"absolute inset-y-0 left-0 w-1\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n <div class=\"border-b border-white/10 px-3 py-2 pl-4\">\n <p class=\"m-0 text-body-sm font-semibold\">{{ item.label }}</p>\n </div>\n @if (item.children?.length) {\n <ul class=\"m-0 list-none space-y-0.5 p-1.5\">\n @for (child of item.children; track child.id) {\n <li>\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"\n child.disabled ? null : child.routerLink\n \"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n class=\"relative flex items-center gap-2 rounded px-2.5 py-1.5 text-body-sm text-white/85 no-underline transition-colors hover:bg-white/10 hover:text-white\"\n [class.opacity-50]=\"child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"relative flex w-full items-center gap-2 rounded px-2.5 py-1.5 text-left text-body-sm text-white/85 transition-colors hover:bg-white/10 hover:text-white\"\n [disabled]=\"!!child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n }\n </div>\n }\n </li>\n }\n </ul>\n </nav>\n\n <ng-content select=\"[footer]\" />\n </aside>\n\n <ng-template #rowInner let-item>\n <span class=\"inline-flex size-5 shrink-0 items-center justify-center\">\n @if (item.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"16\" />\n } @else {\n <span\n class=\"size-1.5 rounded-full bg-current opacity-70\"\n aria-hidden=\"true\"\n ></span>\n }\n </span>\n @if (!collapsed()) {\n <span class=\"min-w-0 flex-1 truncate text-left\">{{ item.label }}</span>\n @if (item.children?.length) {\n <africanies-icon\n name=\"chevron-down\"\n [size]=\"14\"\n class=\"shrink-0 opacity-70 transition-transform duration-200 ease-out\"\n [class.rotate-180]=\"isBranchOpen(item.id)\"\n />\n }\n }\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: item.badge,\n compact: collapsed(),\n }\"\n />\n </ng-template>\n\n <ng-template #navBadge let-visible=\"visible\" let-compact=\"compact\">\n @if (visible) {\n <span\n class=\"pointer-events-none absolute z-10 overflow-visible\"\n [class]=\"compact ? 'right-0.5 top-0.5 h-2 w-2' : 'right-1.5 top-1 h-2 w-2'\"\n aria-hidden=\"true\"\n >\n <span\n class=\"africanies-side-nav-badge-ping absolute left-1/2 top-1/2 size-2 rounded-full bg-danger\"\n ></span>\n <span\n class=\"absolute left-1/2 top-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-danger ring-2 ring-white dark:ring-ink\"\n ></span>\n </span>\n }\n </ng-template>", styles: ["@keyframes africanies-side-nav-badge-pulse{0%{transform:translate(-50%,-50%) scale(.85);opacity:.9}70%{transform:translate(-50%,-50%) scale(2.4);opacity:0}to{transform:translate(-50%,-50%) scale(2.4);opacity:0}}.africanies-side-nav-badge-ping{animation:africanies-side-nav-badge-pulse 1.4s cubic-bezier(0,0,.2,1) infinite}@media(prefers-reduced-motion:reduce){.africanies-side-nav-badge-ping{animation:none;opacity:.5}}\n"], dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14189
14464
  }
14190
14465
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SideNavComponent, decorators: [{
14191
14466
  type: Component,
14192
14467
  args: [{ selector: 'africanies-side-nav', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AfricaniesIconComponent, RouterLink, NgTemplateOutlet], host: {
14193
14468
  class: 'relative z-20 block h-full min-h-0',
14194
- }, template: " <aside\n class=\"relative flex h-full min-h-0 flex-col overflow-visible border-r border-border bg-surface transition-[width] duration-200 ease-out dark:border-white/10 dark:bg-ink-950\"\n [style.width]=\"collapsed() ? '3.75rem' : '15rem'\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n <div\n class=\"pointer-events-none absolute inset-y-0 left-0 w-[3px] transition-colors\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n\n <div\n class=\"sticky top-0 z-20 flex shrink-0 items-center gap-2 border-b border-border bg-surface py-3 dark:border-white/10 dark:bg-ink-950\"\n [class.flex-col]=\"collapsed()\"\n [class.px-3]=\"collapsed()\"\n [class.pl-4]=\"!collapsed()\"\n [class.pr-2]=\"!collapsed()\"\n >\n @if (collapsed()) {\n @if (showLogo()) {\n <img\n [src]=\"resolvedMiniLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"size-8 shrink-0 object-contain\"\n />\n }\n } @else {\n @if (showLogo()) {\n <div class=\"min-w-0 flex-1\">\n <img\n [src]=\"resolvedFullLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"h-9 max-w-full object-contain object-left\"\n />\n </div>\n } @else {\n <div\n class=\"min-w-0 flex-1 truncate text-body-sm font-semibold text-ink dark:text-white\"\n >\n <ng-content select=\"[brand]\" />\n </div>\n }\n }\n <button\n type=\"button\"\n class=\"inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white\"\n data-onboarding=\"nav-collapse\"\n [attr.aria-label]=\"\n collapsed() ? 'Expand navigation' : 'Collapse navigation'\n \"\n [attr.aria-expanded]=\"!collapsed()\"\n (click)=\"toggleCollapsed()\"\n >\n <africanies-icon\n [name]=\"collapsed() ? 'angle-double-right' : 'angle-double-left'\"\n [size]=\"16\"\n />\n </button>\n </div>\n\n <nav\n class=\"africanies-overlay-scroll relative min-h-0 flex-1 overflow-y-auto overflow-x-visible px-3 py-3\"\n >\n @if (!collapsed() && expandParents() && hasOpenBranches()) {\n <!-- Zero-height sticky host: keeps the control pinned without reserving a row. -->\n <div class=\"sticky top-0 z-10 h-0 overflow-visible\">\n <button\n type=\"button\"\n class=\"absolute right-0 top-0 inline-flex size-7 cursor-pointer items-center justify-center rounded-md bg-surface text-neutral-600 shadow-sm ring-1 ring-border-strong transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:bg-ink-950 dark:text-neutral-400 dark:ring-white/15 dark:hover:bg-white/10 dark:hover:text-white\"\n data-onboarding=\"nav-branches-collapse\"\n aria-label=\"Collapse all\"\n (click)=\"collapseAllBranches()\"\n >\n <africanies-icon name=\"compress\" [size]=\"14\" />\n </button>\n </div>\n }\n <ul class=\"m-0 flex list-none flex-col gap-0.5 p-0\">\n @for (item of items(); track item.id) {\n <li\n class=\"relative overflow-visible last:mb-0\"\n [class.mb-3]=\"!collapsed() && !item.children?.length\"\n (mouseenter)=\"onItemEnter(item.id)\"\n (mouseleave)=\"onItemLeave(item.id)\"\n >\n @if (item.routerLink !== null && item.routerLink !== undefined) {\n <a\n [routerLink]=\"item.disabled ? null : item.routerLink\"\n [queryParams]=\"item.queryParams\"\n [fragment]=\"item.fragment\"\n [class]=\"rowClass(item)\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n [attr.aria-disabled]=\"item.disabled ? true : null\"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"rowClass(item)\"\n [disabled]=\"!!item.disabled\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </button>\n }\n\n @if (!collapsed() && item.children?.length) {\n <div\n class=\"grid transition-[grid-template-rows] duration-200 ease-out\"\n [style.grid-template-rows]=\"\n isBranchOpen(item.id) ? '1fr' : '0fr'\n \"\n [attr.aria-hidden]=\"!isBranchOpen(item.id)\"\n [class.pointer-events-none]=\"!isBranchOpen(item.id)\"\n >\n <ul\n class=\"relative m-0 mt-0.5 min-h-0 list-none space-y-0.5 overflow-hidden py-1 pl-4 before:absolute before:bottom-1 before:left-[1.35rem] before:top-0 before:w-px before:bg-border dark:before:bg-white/15\"\n >\n @for (child of item.children; track child.id) {\n <li\n class=\"transition-opacity duration-200 ease-out\"\n [class.opacity-0]=\"!isBranchOpen(item.id)\"\n [class.opacity-100]=\"isBranchOpen(item.id)\"\n >\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"child.disabled ? null : child.routerLink\"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n [class]=\"childRowClass(child)\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"childRowClass(child)\"\n [disabled]=\"!!child.disabled\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n </div>\n }\n\n @if (collapsed() && hoverId() === item.id) {\n <div\n class=\"absolute left-full top-0 z-50 ml-2 min-w-[11rem] overflow-hidden rounded-md border border-border bg-ink text-white shadow-lg dark:border-white/15\"\n role=\"tooltip\"\n >\n <div\n class=\"absolute inset-y-0 left-0 w-1\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n <div class=\"border-b border-white/10 px-3 py-2 pl-4\">\n <p class=\"m-0 text-body-sm font-semibold\">{{ item.label }}</p>\n </div>\n @if (item.children?.length) {\n <ul class=\"m-0 list-none space-y-0.5 p-1.5\">\n @for (child of item.children; track child.id) {\n <li>\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"\n child.disabled ? null : child.routerLink\n \"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n class=\"relative flex items-center gap-2 rounded px-2.5 py-1.5 text-body-sm text-white/85 no-underline transition-colors hover:bg-white/10 hover:text-white\"\n [class.opacity-50]=\"child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"relative flex w-full items-center gap-2 rounded px-2.5 py-1.5 text-left text-body-sm text-white/85 transition-colors hover:bg-white/10 hover:text-white\"\n [disabled]=\"!!child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n }\n </div>\n }\n </li>\n }\n </ul>\n </nav>\n\n <ng-content select=\"[footer]\" />\n </aside>\n\n <ng-template #rowInner let-item>\n <span class=\"inline-flex size-5 shrink-0 items-center justify-center\">\n @if (item.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"16\" />\n } @else {\n <span\n class=\"size-1.5 rounded-full bg-current opacity-70\"\n aria-hidden=\"true\"\n ></span>\n }\n </span>\n @if (!collapsed()) {\n <span class=\"min-w-0 flex-1 truncate text-left\">{{ item.label }}</span>\n @if (item.children?.length) {\n <africanies-icon\n name=\"chevron-down\"\n [size]=\"14\"\n class=\"shrink-0 opacity-70 transition-transform duration-200 ease-out\"\n [class.rotate-180]=\"isBranchOpen(item.id)\"\n />\n }\n }\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: item.badge,\n compact: collapsed(),\n }\"\n />\n </ng-template>\n\n <ng-template #navBadge let-visible=\"visible\" let-compact=\"compact\">\n @if (visible) {\n <span\n class=\"pointer-events-none absolute z-10 overflow-visible\"\n [class]=\"compact ? 'right-0.5 top-0.5 h-2 w-2' : 'right-1.5 top-1 h-2 w-2'\"\n aria-hidden=\"true\"\n >\n <span\n class=\"africanies-side-nav-badge-ping absolute left-1/2 top-1/2 size-2 rounded-full bg-danger\"\n ></span>\n <span\n class=\"absolute left-1/2 top-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-danger ring-2 ring-white dark:ring-ink\"\n ></span>\n </span>\n }\n </ng-template>", styles: ["@keyframes africanies-side-nav-badge-pulse{0%{transform:translate(-50%,-50%) scale(.85);opacity:.9}70%{transform:translate(-50%,-50%) scale(2.4);opacity:0}to{transform:translate(-50%,-50%) scale(2.4);opacity:0}}.africanies-side-nav-badge-ping{animation:africanies-side-nav-badge-pulse 1.4s cubic-bezier(0,0,.2,1) infinite}@media(prefers-reduced-motion:reduce){.africanies-side-nav-badge-ping{animation:none;opacity:.5}}\n"] }]
14469
+ }, template: " <aside\n class=\"relative flex h-full min-h-0 flex-col overflow-visible border-r border-border bg-surface transition-[width] duration-200 ease-out dark:border-white/10 dark:bg-ink-950\"\n [style.width]=\"collapsed() ? '3.75rem' : '15rem'\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n <div\n class=\"pointer-events-none absolute inset-y-0 left-0 w-[3px] transition-colors\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n\n <div\n class=\"sticky top-0 z-20 flex shrink-0 items-center gap-2 border-b border-border bg-surface py-3 dark:border-white/10 dark:bg-ink-950\"\n [class.flex-col]=\"collapsed()\"\n [class.px-3]=\"collapsed()\"\n [class.pl-4]=\"!collapsed()\"\n [class.pr-2]=\"!collapsed()\"\n >\n @if (collapsed()) {\n @if (showLogo()) {\n <img\n [src]=\"resolvedMiniLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"size-8 shrink-0 object-contain\"\n />\n }\n } @else {\n @if (showLogo()) {\n <div class=\"min-w-0 flex-1\">\n <img\n [src]=\"resolvedFullLogo()\"\n alt=\"African Import Export Solutions\"\n class=\"h-9 max-w-full object-contain object-left\"\n />\n </div>\n } @else {\n <div\n class=\"min-w-0 flex-1 truncate text-body-sm font-semibold text-ink dark:text-white\"\n >\n <ng-content select=\"[brand]\" />\n </div>\n }\n }\n <button\n type=\"button\"\n class=\"inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white\"\n data-onboarding=\"nav-collapse\"\n [attr.aria-label]=\"\n collapsed() ? 'Expand navigation' : 'Collapse navigation'\n \"\n [attr.aria-expanded]=\"!collapsed()\"\n (click)=\"toggleCollapsed()\"\n >\n <africanies-icon\n [name]=\"collapsed() ? 'angle-double-right' : 'angle-double-left'\"\n [size]=\"16\"\n />\n </button>\n </div>\n\n <ng-content select=\"[pre-nav]\" />\n\n <nav\n class=\"africanies-overlay-scroll relative min-h-0 flex-1 overflow-y-auto overflow-x-visible px-3 py-3\"\n >\n @if (!collapsed() && expandParents() && hasOpenBranches()) {\n <!-- Zero-height sticky host: keeps the control pinned without reserving a row. -->\n <div class=\"sticky top-0 z-20 h-0 overflow-visible\">\n <button\n type=\"button\"\n class=\"absolute -right-2 -top-1 inline-flex size-7 cursor-pointer items-center justify-center rounded-md bg-surface text-neutral-600 shadow-sm ring-1 ring-border-strong transition-colors hover:bg-background-hover hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:bg-ink-950 dark:text-neutral-400 dark:ring-white/15 dark:hover:bg-ink-surface dark:hover:text-white\"\n data-onboarding=\"nav-branches-collapse\"\n aria-label=\"Collapse all\"\n (click)=\"collapseAllBranches()\"\n >\n <africanies-icon name=\"compress\" [size]=\"14\" />\n </button>\n </div>\n }\n <ul class=\"m-0 flex list-none flex-col gap-0.5 p-0\">\n @for (item of items(); track item.id) {\n <li\n class=\"relative overflow-visible last:mb-0\"\n [class.mb-3]=\"!collapsed() && !item.children?.length\"\n (mouseenter)=\"onItemEnter(item.id)\"\n (mouseleave)=\"onItemLeave(item.id)\"\n >\n @if (item.routerLink !== null && item.routerLink !== undefined) {\n <a\n [routerLink]=\"item.disabled ? null : item.routerLink\"\n [queryParams]=\"item.queryParams\"\n [fragment]=\"item.fragment\"\n [class]=\"rowClass(item)\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n [attr.aria-disabled]=\"item.disabled ? true : null\"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"rowClass(item)\"\n [disabled]=\"!!item.disabled\"\n [attr.aria-current]=\"isLeafActive(item) ? 'page' : null\"\n [attr.aria-expanded]=\"\n item.children?.length ? isBranchOpen(item.id) : null\n \"\n (click)=\"onActivate(item, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowInner\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n />\n </button>\n }\n\n @if (!collapsed() && item.children?.length) {\n <div\n class=\"grid transition-[grid-template-rows] duration-200 ease-out\"\n [style.grid-template-rows]=\"\n isBranchOpen(item.id) ? '1fr' : '0fr'\n \"\n [attr.aria-hidden]=\"!isBranchOpen(item.id)\"\n [class.pointer-events-none]=\"!isBranchOpen(item.id)\"\n >\n <ul\n class=\"relative m-0 mt-0.5 min-h-0 list-none space-y-0.5 overflow-hidden py-1 pl-4 before:absolute before:bottom-1 before:left-[1.35rem] before:top-0 before:w-px before:bg-border dark:before:bg-white/15\"\n >\n @for (child of item.children; track child.id) {\n <li\n class=\"transition-opacity duration-200 ease-out\"\n [class.opacity-0]=\"!isBranchOpen(item.id)\"\n [class.opacity-100]=\"isBranchOpen(item.id)\"\n >\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"child.disabled ? null : child.routerLink\"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n [class]=\"childRowClass(child)\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n [class]=\"childRowClass(child)\"\n [disabled]=\"!!child.disabled\"\n [attr.aria-current]=\"\n isLeafActive(child) ? 'page' : null\n \"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon\n [name]=\"icon\"\n [size]=\"14\"\n class=\"shrink-0 opacity-80\"\n />\n }\n <span class=\"truncate\">{{ child.label }}</span>\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: false,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n </div>\n }\n\n @if (collapsed() && hoverId() === item.id) {\n <div\n class=\"absolute left-full top-0 z-50 ml-2 min-w-[11rem] overflow-hidden rounded-md border border-border bg-ink text-white shadow-lg dark:border-white/15\"\n role=\"tooltip\"\n >\n <div\n class=\"absolute inset-y-0 left-0 w-1\"\n [class]=\"modeColor.classes().bg\"\n aria-hidden=\"true\"\n ></div>\n <div class=\"border-b border-white/10 px-3 py-2 pl-4\">\n <p class=\"m-0 text-body-sm font-semibold\">{{ item.label }}</p>\n </div>\n @if (item.children?.length) {\n <ul class=\"m-0 list-none space-y-0.5 p-1.5\">\n @for (child of item.children; track child.id) {\n <li>\n @if (\n child.routerLink !== null &&\n child.routerLink !== undefined\n ) {\n <a\n [routerLink]=\"\n child.disabled ? null : child.routerLink\n \"\n [queryParams]=\"child.queryParams\"\n [fragment]=\"child.fragment\"\n class=\"relative flex items-center gap-2 rounded px-2.5 py-1.5 text-body-sm text-white/85 no-underline transition-colors hover:bg-white/10 hover:text-white\"\n [class.opacity-50]=\"child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"relative flex w-full items-center gap-2 rounded px-2.5 py-1.5 text-left text-body-sm text-white/85 transition-colors hover:bg-white/10 hover:text-white\"\n [disabled]=\"!!child.disabled\"\n (click)=\"onActivate(child, $event)\"\n >\n @if (child.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"14\" />\n }\n {{ child.label }}\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: child.badge,\n compact: true,\n }\"\n />\n </button>\n }\n </li>\n }\n </ul>\n }\n </div>\n }\n </li>\n }\n </ul>\n </nav>\n\n <ng-content select=\"[footer]\" />\n </aside>\n\n <ng-template #rowInner let-item>\n <span class=\"inline-flex size-5 shrink-0 items-center justify-center\">\n @if (item.icon; as icon) {\n <africanies-icon [name]=\"icon\" [size]=\"16\" />\n } @else {\n <span\n class=\"size-1.5 rounded-full bg-current opacity-70\"\n aria-hidden=\"true\"\n ></span>\n }\n </span>\n @if (!collapsed()) {\n <span class=\"min-w-0 flex-1 truncate text-left\">{{ item.label }}</span>\n @if (item.children?.length) {\n <africanies-icon\n name=\"chevron-down\"\n [size]=\"14\"\n class=\"shrink-0 opacity-70 transition-transform duration-200 ease-out\"\n [class.rotate-180]=\"isBranchOpen(item.id)\"\n />\n }\n }\n <ng-container\n [ngTemplateOutlet]=\"navBadge\"\n [ngTemplateOutletContext]=\"{\n visible: item.badge,\n compact: collapsed(),\n }\"\n />\n </ng-template>\n\n <ng-template #navBadge let-visible=\"visible\" let-compact=\"compact\">\n @if (visible) {\n <span\n class=\"pointer-events-none absolute z-10 overflow-visible\"\n [class]=\"compact ? 'right-0.5 top-0.5 h-2 w-2' : 'right-1.5 top-1 h-2 w-2'\"\n aria-hidden=\"true\"\n >\n <span\n class=\"africanies-side-nav-badge-ping absolute left-1/2 top-1/2 size-2 rounded-full bg-danger\"\n ></span>\n <span\n class=\"absolute left-1/2 top-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-danger ring-2 ring-white dark:ring-ink\"\n ></span>\n </span>\n }\n </ng-template>", styles: ["@keyframes africanies-side-nav-badge-pulse{0%{transform:translate(-50%,-50%) scale(.85);opacity:.9}70%{transform:translate(-50%,-50%) scale(2.4);opacity:0}to{transform:translate(-50%,-50%) scale(2.4);opacity:0}}.africanies-side-nav-badge-ping{animation:africanies-side-nav-badge-pulse 1.4s cubic-bezier(0,0,.2,1) infinite}@media(prefers-reduced-motion:reduce){.africanies-side-nav-badge-ping{animation:none;opacity:.5}}\n"] }]
14195
14470
  }], 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 }] }] } });
14196
14471
 
14197
14472
  /**
@@ -14931,28 +15206,32 @@ class AvatarMenuComponent {
14931
15206
  showCaret = input(true, { ...(ngDevMode ? { debugName: "showCaret" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
14932
15207
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AvatarMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14933
15208
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AvatarMenuComponent, isStandalone: true, selector: "africanies-avatar-menu", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, menuItems: { classPropertyName: "menuItems", publicName: "menuItems", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showCaret: { classPropertyName: "showCaret", publicName: "showCaret", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
14934
- <africanies-action-menu
14935
- [items]="menuItems()"
14936
- [ariaLabel]="ariaLabel()"
14937
- [disabled]="disabled()"
14938
- >
14939
- <button
14940
- type="button"
14941
- africaniesActionMenuTrigger
14942
- class="inline-flex cursor-pointer items-center gap-1 rounded-lg p-0.5 pr-1 transition-colors hover:bg-background-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus disabled:cursor-not-allowed disabled:opacity-50 dark:hover:bg-white/10"
14943
- [attr.aria-label]="ariaLabel()"
15209
+ @if (menuItems().length > 0) {
15210
+ <africanies-action-menu
15211
+ [items]="menuItems()"
15212
+ [ariaLabel]="ariaLabel()"
15213
+ [disabled]="disabled()"
14944
15214
  >
14945
- <africanies-avatar [name]="name()" [src]="src()" [size]="size()" />
14946
- @if (showCaret()) {
14947
- <africanies-icon
14948
- name="chevron-down"
14949
- [size]="14"
14950
- class="shrink-0 text-neutral-600 dark:text-neutral-400"
14951
- aria-hidden="true"
14952
- />
14953
- }
14954
- </button>
14955
- </africanies-action-menu>
15215
+ <button
15216
+ type="button"
15217
+ africaniesActionMenuTrigger
15218
+ class="inline-flex cursor-pointer items-center gap-1 rounded-lg p-0.5 pr-1 transition-colors hover:bg-background-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus disabled:cursor-not-allowed disabled:opacity-50 dark:hover:bg-white/10"
15219
+ [attr.aria-label]="ariaLabel()"
15220
+ >
15221
+ <africanies-avatar [name]="name()" [src]="src()" [size]="size()" />
15222
+ @if (showCaret()) {
15223
+ <africanies-icon
15224
+ name="chevron-down"
15225
+ [size]="14"
15226
+ class="shrink-0 text-neutral-600 dark:text-neutral-400"
15227
+ aria-hidden="true"
15228
+ />
15229
+ }
15230
+ </button>
15231
+ </africanies-action-menu>
15232
+ } @else {
15233
+ <africanies-avatar [name]="name()" [src]="src()" [size]="size()" />
15234
+ }
14956
15235
  `, isInline: true, dependencies: [{ kind: "component", type: AvatarComponent, selector: "africanies-avatar", inputs: ["name", "src", "alt", "size"] }, { kind: "component", type: ActionMenuComponent, selector: "africanies-action-menu", inputs: ["items", "ariaLabel", "disabled"] }, { kind: "directive", type: ActionMenuTriggerDirective, selector: "[africaniesActionMenuTrigger]" }, { kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14957
15236
  }
14958
15237
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AvatarMenuComponent, decorators: [{
@@ -14968,28 +15247,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
14968
15247
  AfricaniesIconComponent,
14969
15248
  ],
14970
15249
  template: `
14971
- <africanies-action-menu
14972
- [items]="menuItems()"
14973
- [ariaLabel]="ariaLabel()"
14974
- [disabled]="disabled()"
14975
- >
14976
- <button
14977
- type="button"
14978
- africaniesActionMenuTrigger
14979
- class="inline-flex cursor-pointer items-center gap-1 rounded-lg p-0.5 pr-1 transition-colors hover:bg-background-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus disabled:cursor-not-allowed disabled:opacity-50 dark:hover:bg-white/10"
14980
- [attr.aria-label]="ariaLabel()"
15250
+ @if (menuItems().length > 0) {
15251
+ <africanies-action-menu
15252
+ [items]="menuItems()"
15253
+ [ariaLabel]="ariaLabel()"
15254
+ [disabled]="disabled()"
14981
15255
  >
14982
- <africanies-avatar [name]="name()" [src]="src()" [size]="size()" />
14983
- @if (showCaret()) {
14984
- <africanies-icon
14985
- name="chevron-down"
14986
- [size]="14"
14987
- class="shrink-0 text-neutral-600 dark:text-neutral-400"
14988
- aria-hidden="true"
14989
- />
14990
- }
14991
- </button>
14992
- </africanies-action-menu>
15256
+ <button
15257
+ type="button"
15258
+ africaniesActionMenuTrigger
15259
+ class="inline-flex cursor-pointer items-center gap-1 rounded-lg p-0.5 pr-1 transition-colors hover:bg-background-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus disabled:cursor-not-allowed disabled:opacity-50 dark:hover:bg-white/10"
15260
+ [attr.aria-label]="ariaLabel()"
15261
+ >
15262
+ <africanies-avatar [name]="name()" [src]="src()" [size]="size()" />
15263
+ @if (showCaret()) {
15264
+ <africanies-icon
15265
+ name="chevron-down"
15266
+ [size]="14"
15267
+ class="shrink-0 text-neutral-600 dark:text-neutral-400"
15268
+ aria-hidden="true"
15269
+ />
15270
+ }
15271
+ </button>
15272
+ </africanies-action-menu>
15273
+ } @else {
15274
+ <africanies-avatar [name]="name()" [src]="src()" [size]="size()" />
15275
+ }
14993
15276
  `,
14994
15277
  }]
14995
15278
  }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], menuItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuItems", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showCaret: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCaret", required: false }] }] } });
@@ -16092,10 +16375,10 @@ function hashString(value) {
16092
16375
  }
16093
16376
 
16094
16377
  /** Bump when cache shape or city resolution strategy changes. */
16095
- const CACHE_KEY = 'africanies-header-weather-v4';
16378
+ const CACHE_KEY = 'africanies-header-weather-v7';
16096
16379
  const FETCH_MS = 4_000;
16097
16380
  const BROWSER_GEO_MS = 3_500;
16098
- /** IP fallback — city string is often wrong (e.g. NG IPs labelled Lagos). */
16381
+ /** IP fallback — city string is used only when reverse-geocode is empty. */
16099
16382
  const GEO_URL = 'https://get.geojs.io/v1/ip/geo.json';
16100
16383
  /** Client reverse-geocode so the label matches the forecast coordinates. */
16101
16384
  const REVERSE_GEO_URL = 'https://api.bigdatacloud.net/data/reverse-geocode-client';
@@ -16142,9 +16425,13 @@ function mapWmoWeatherCode(code) {
16142
16425
  * Short condition label for the header weather chip.
16143
16426
  *
16144
16427
  * @param kind - Coarse forecast kind.
16145
- * @returns Display label such as `Rain` or `Clear`.
16428
+ * @param hour - Local hour; clear days show as Sunny, nights as Clear.
16429
+ * @returns Display label such as `Rain` or `Sunny`.
16146
16430
  */
16147
- function headerWeatherLabel(kind) {
16431
+ function headerWeatherLabel(kind, hour = new Date().getHours()) {
16432
+ if (kind === 'clear') {
16433
+ return hour >= 19 || hour < 6 ? 'Clear' : 'Sunny';
16434
+ }
16148
16435
  return WEATHER_LABELS[kind];
16149
16436
  }
16150
16437
  /**
@@ -16168,8 +16455,7 @@ function headerWeatherIcon(kind, hour) {
16168
16455
  *
16169
16456
  * Coordinates prefer the browser Geolocation API (accurate for travellers;
16170
16457
  * may prompt once). Otherwise IP geo is used. The city label is reverse-
16171
- * geocoded from those coordinates IP `city` strings are often wrong for
16172
- * Nigeria (many networks resolve as Lagos).
16458
+ * geocoded from those coordinates, with the IP city as a fallback.
16173
16459
  *
16174
16460
  * Fails closed: missing browser APIs, timeouts, and HTTP errors all return
16175
16461
  * `null` so the greeting can stay time-of-day only. Cached per local hour
@@ -16232,10 +16518,16 @@ function readCache(hour) {
16232
16518
  if (parsed.hour !== hour || !parsed.kind) {
16233
16519
  return null;
16234
16520
  }
16521
+ // City-less entries (e.g. browser geo + failed reverse) used to stick for
16522
+ // the hour — treat them as a miss so we can recover the location label.
16523
+ const city = asCity(parsed.city);
16524
+ if (!city) {
16525
+ return null;
16526
+ }
16235
16527
  return {
16236
16528
  kind: parsed.kind,
16237
16529
  temperatureC: parsed.temperatureC,
16238
- city: parsed.city,
16530
+ city,
16239
16531
  };
16240
16532
  }
16241
16533
  catch {
@@ -16252,26 +16544,32 @@ function writeCache(value) {
16252
16544
  }
16253
16545
  /**
16254
16546
  * Prefer device coordinates when available (may prompt once); otherwise fall
16255
- * back to IP geo. IP city labels for Nigeria are often wrong reverse
16256
- * geocoding from these coordinates still depends on accurate lat/lon.
16547
+ * back to IP geo. IP is always fetched in parallel so its city can backfill
16548
+ * the label when reverse-geocode is empty (browser geo has no city string).
16257
16549
  */
16258
16550
  async function resolveCoordinates(fetchFn) {
16259
- const browser = await tryBrowserGeolocation();
16551
+ const [browser, ipGeo] = await Promise.all([
16552
+ tryBrowserGeolocation(),
16553
+ fetchIpGeo(fetchFn),
16554
+ ]);
16555
+ const ipCity = asCity(ipGeo?.city);
16260
16556
  if (browser) {
16261
- return browser;
16557
+ return { ...browser, fallbackCity: ipCity };
16262
16558
  }
16263
- const geo = (await fetchJson(fetchFn, GEO_URL));
16264
- const latitude = asNumber(geo?.latitude);
16265
- const longitude = asNumber(geo?.longitude);
16559
+ const latitude = asNumber(ipGeo?.latitude);
16560
+ const longitude = asNumber(ipGeo?.longitude);
16266
16561
  if (latitude === null || longitude === null) {
16267
16562
  return null;
16268
16563
  }
16269
16564
  return {
16270
16565
  latitude,
16271
16566
  longitude,
16272
- fallbackCity: asCity(geo?.city),
16567
+ fallbackCity: ipCity,
16273
16568
  };
16274
16569
  }
16570
+ async function fetchIpGeo(fetchFn) {
16571
+ return (await fetchJson(fetchFn, GEO_URL));
16572
+ }
16275
16573
  async function tryBrowserGeolocation() {
16276
16574
  if (typeof navigator === 'undefined' ||
16277
16575
  typeof navigator.geolocation?.getCurrentPosition !== 'function') {
@@ -16456,26 +16754,33 @@ class AppShellHeaderComponent {
16456
16754
  if (temp === undefined || !Number.isFinite(temp)) {
16457
16755
  return null;
16458
16756
  }
16459
- return temp.toFixed(1);
16757
+ return String(Math.round(temp));
16460
16758
  }, /* @ts-ignore */
16461
16759
  ...(ngDevMode ? [{ debugName: "weatherTemp" }] : /* istanbul ignore next */ []));
16462
- weatherPlace = computed(() => {
16760
+ weatherCondition = computed(() => {
16463
16761
  const forecast = this.weather();
16464
16762
  if (!forecast) {
16465
16763
  return '';
16466
16764
  }
16467
- return forecast.city?.trim() || headerWeatherLabel(forecast.kind);
16765
+ return headerWeatherLabel(forecast.kind, this.now().getHours());
16766
+ }, /* @ts-ignore */
16767
+ ...(ngDevMode ? [{ debugName: "weatherCondition" }] : /* istanbul ignore next */ []));
16768
+ weatherCity = computed(() => {
16769
+ const city = this.weather()?.city?.trim();
16770
+ return city || null;
16468
16771
  }, /* @ts-ignore */
16469
- ...(ngDevMode ? [{ debugName: "weatherPlace" }] : /* istanbul ignore next */ []));
16772
+ ...(ngDevMode ? [{ debugName: "weatherCity" }] : /* istanbul ignore next */ []));
16470
16773
  weatherAriaLabel = computed(() => {
16471
16774
  const forecast = this.weather();
16472
16775
  if (!forecast) {
16473
16776
  return 'Weather';
16474
16777
  }
16475
- const condition = headerWeatherLabel(forecast.kind).toLowerCase();
16778
+ const condition = this.weatherCondition().toLowerCase();
16476
16779
  const temp = this.weatherTemp();
16477
- const city = forecast.city?.trim();
16478
- const degrees = temp === null ? condition : `${temp} degrees, ${condition}`;
16780
+ const city = this.weatherCity();
16781
+ const degrees = temp === null
16782
+ ? condition
16783
+ : `${temp} degrees Celsius, ${condition}`;
16479
16784
  return city ? `Weather in ${city}: ${degrees}` : `Weather: ${degrees}`;
16480
16785
  }, /* @ts-ignore */
16481
16786
  ...(ngDevMode ? [{ debugName: "weatherAriaLabel" }] : /* istanbul ignore next */ []));
@@ -16577,13 +16882,15 @@ class AppShellHeaderComponent {
16577
16882
  <span
16578
16883
  class="text-body-sm font-medium tabular-nums tracking-tight text-ink dark:text-white"
16579
16884
  >
16580
- {{ temp }}°
16885
+ {{ temp }}°C
16581
16886
  </span>
16582
16887
  }
16583
16888
  <span
16584
16889
  class="hidden text-caption text-neutral-600 md:inline dark:text-neutral-400"
16585
16890
  >
16586
- {{ weatherPlace() }}
16891
+ {{ weatherCondition() }}@if (weatherCity(); as city) {
16892
+ · {{ city }}
16893
+ }
16587
16894
  </span>
16588
16895
  </div>
16589
16896
  }
@@ -16642,18 +16949,26 @@ class AppShellHeaderComponent {
16642
16949
  }
16643
16950
 
16644
16951
  @if (userName()) {
16645
- <africanies-avatar-menu
16646
- [name]="userName()!"
16647
- [src]="userAvatarSrc()"
16648
- [menuItems]="userMenuItems()"
16649
- [size]="'md'"
16650
- />
16952
+ @if (userMenuItems().length > 0) {
16953
+ <africanies-avatar-menu
16954
+ [name]="userName()!"
16955
+ [src]="userAvatarSrc()"
16956
+ [menuItems]="userMenuItems()"
16957
+ [size]="'md'"
16958
+ />
16959
+ } @else {
16960
+ <africanies-avatar
16961
+ [name]="userName()!"
16962
+ [src]="userAvatarSrc()"
16963
+ size="md"
16964
+ />
16965
+ }
16651
16966
  }
16652
16967
  </div>
16653
16968
  }
16654
16969
  </div>
16655
16970
  </div>
16656
- `, isInline: true, dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "component", type: AvatarMenuComponent, selector: "africanies-avatar-menu", inputs: ["name", "src", "menuItems", "size", "ariaLabel", "disabled", "showCaret"] }, { kind: "pipe", type: DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
16971
+ `, isInline: true, dependencies: [{ kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "component", type: AvatarComponent, selector: "africanies-avatar", inputs: ["name", "src", "alt", "size"] }, { kind: "component", type: AvatarMenuComponent, selector: "africanies-avatar-menu", inputs: ["name", "src", "menuItems", "size", "ariaLabel", "disabled", "showCaret"] }, { kind: "pipe", type: DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
16657
16972
  }
16658
16973
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AppShellHeaderComponent, decorators: [{
16659
16974
  type: Component,
@@ -16661,7 +16976,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16661
16976
  selector: 'africanies-app-shell-header',
16662
16977
  standalone: true,
16663
16978
  changeDetection: ChangeDetectionStrategy.OnPush,
16664
- imports: [AfricaniesIconComponent, AvatarMenuComponent, DatePipe],
16979
+ imports: [
16980
+ AfricaniesIconComponent,
16981
+ AvatarComponent,
16982
+ AvatarMenuComponent,
16983
+ DatePipe,
16984
+ ],
16665
16985
  template: `
16666
16986
  <div
16667
16987
  class="flex min-w-0 items-center gap-2 px-3 py-2.5 sm:gap-4 sm:px-6 sm:py-3"
@@ -16704,13 +17024,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16704
17024
  <span
16705
17025
  class="text-body-sm font-medium tabular-nums tracking-tight text-ink dark:text-white"
16706
17026
  >
16707
- {{ temp }}°
17027
+ {{ temp }}°C
16708
17028
  </span>
16709
17029
  }
16710
17030
  <span
16711
17031
  class="hidden text-caption text-neutral-600 md:inline dark:text-neutral-400"
16712
17032
  >
16713
- {{ weatherPlace() }}
17033
+ {{ weatherCondition() }}@if (weatherCity(); as city) {
17034
+ · {{ city }}
17035
+ }
16714
17036
  </span>
16715
17037
  </div>
16716
17038
  }
@@ -16769,12 +17091,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16769
17091
  }
16770
17092
 
16771
17093
  @if (userName()) {
16772
- <africanies-avatar-menu
16773
- [name]="userName()!"
16774
- [src]="userAvatarSrc()"
16775
- [menuItems]="userMenuItems()"
16776
- [size]="'md'"
16777
- />
17094
+ @if (userMenuItems().length > 0) {
17095
+ <africanies-avatar-menu
17096
+ [name]="userName()!"
17097
+ [src]="userAvatarSrc()"
17098
+ [menuItems]="userMenuItems()"
17099
+ [size]="'md'"
17100
+ />
17101
+ } @else {
17102
+ <africanies-avatar
17103
+ [name]="userName()!"
17104
+ [src]="userAvatarSrc()"
17105
+ size="md"
17106
+ />
17107
+ }
16778
17108
  }
16779
17109
  </div>
16780
17110
  }
@@ -17684,8 +18014,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17684
18014
  * `<ng-template africaniesCellDef="key">` templates. Columns without a matching
17685
18015
  * template fall back to rendering `row[key]` as plain text.
17686
18016
  *
17687
- * Optional toolbar: top-left **Refresh** (`showRefresh` {@link refreshClick}).
17688
- * Hidden while the body shows empty or error — those states expose Retry instead.
18017
+ * Optional toolbar: top-left **shipping mode** segment (on by default;
18018
+ * `[showShippingMode]="false"` to hide) and **Refresh** (`showRefresh`
18019
+ * {@link refreshClick}). Refresh is hidden while
18020
+ * the body shows empty or error — those states expose Retry instead.
17689
18021
  * While {@link refreshing} is true the rows stay on screen — the refresh icon
17690
18022
  * spins (do not swap to a blocking loader). Use {@link loading} when the page
17691
18023
  * of data is changing (pagination / size): rows stay on screen, a keep-rows
@@ -17714,8 +18046,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17714
18046
  * and never reorder local rows.
17715
18047
  *
17716
18048
  * **Expandable rows:** project `<ng-template africaniesRowDetail="Label" let-row>`
17717
- * templates. A leading chevron column appears; the expanded panel renders each
17718
- * label with its template value in a responsive grid.
18049
+ * templates. A leading chevron column appears; the expanded panel sticks to the
18050
+ * scrollport width (so it does not scroll horizontally with the columns) and
18051
+ * lays out label/value pairs in a wrapping auto-fill grid.
17719
18052
  *
17720
18053
  * @typeParam T - Row record shape.
17721
18054
  *
@@ -17758,7 +18091,47 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17758
18091
  * page scroll is unchanged.
17759
18092
  */
17760
18093
  class TableComponent {
17761
- modeColor = inject(ModeColorService);
18094
+ destroyRef = inject(DestroyRef);
18095
+ shipping = inject(ShippingModeService);
18096
+ /** Local STN / SFN segment options for {@link showShippingMode}. */
18097
+ shippingModeItems = [
18098
+ { id: 'stn', label: 'Shipping to Nigeria' },
18099
+ { id: 'sfn', label: 'Shipping from Nigeria' },
18100
+ ];
18101
+ /**
18102
+ * Segment selection mirror of {@link ShippingModeService.mode}. Kept local
18103
+ * so a denied {@link ShippingModeService.requestModeChange} can snap the
18104
+ * pills back when the service mode did not change.
18105
+ */
18106
+ shippingModeActiveId = signal('sfn', /* @ts-ignore */
18107
+ ...(ngDevMode ? [{ debugName: "shippingModeActiveId" }] : /* istanbul ignore next */ []));
18108
+ /** Scrollport that owns horizontal overflow for the main grid. */
18109
+ scrollport = viewChild('scrollport', /* @ts-ignore */
18110
+ ...(ngDevMode ? [{ debugName: "scrollport" }] : /* istanbul ignore next */ []));
18111
+ /**
18112
+ * Visible width of {@link scrollport}. Applied to the expand panel so
18113
+ * details stay within the viewport and do not scroll with the columns.
18114
+ */
18115
+ scrollportWidthPx = signal(0, /* @ts-ignore */
18116
+ ...(ngDevMode ? [{ debugName: "scrollportWidthPx" }] : /* istanbul ignore next */ []));
18117
+ constructor() {
18118
+ effect(() => {
18119
+ this.shippingModeActiveId.set(this.shipping.mode());
18120
+ });
18121
+ afterNextRender(() => {
18122
+ const el = this.scrollport()?.nativeElement;
18123
+ if (!el) {
18124
+ return;
18125
+ }
18126
+ const sync = () => {
18127
+ this.scrollportWidthPx.set(el.clientWidth);
18128
+ };
18129
+ sync();
18130
+ const observer = new ResizeObserver(sync);
18131
+ observer.observe(el);
18132
+ this.destroyRef.onDestroy(() => observer.disconnect());
18133
+ });
18134
+ }
17762
18135
  /**
17763
18136
  * When false, hides row expansion even if {@link RowDetailDefDirective}
17764
18137
  * templates are projected. Defaults to true.
@@ -17793,6 +18166,13 @@ class TableComponent {
17793
18166
  */
17794
18167
  sort = input(null, /* @ts-ignore */
17795
18168
  ...(ngDevMode ? [{ debugName: "sort" }] : /* istanbul ignore next */ []));
18169
+ /**
18170
+ * Show the Shipping to / from Nigeria segment above the table (top-left).
18171
+ * On by default; set `[showShippingMode]="false"` to hide (e.g. modals).
18172
+ * Writes through {@link ShippingModeService.requestModeChange} so feature
18173
+ * guards still run.
18174
+ */
18175
+ showShippingMode = input(true, { ...(ngDevMode ? { debugName: "showShippingMode" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
17796
18176
  /**
17797
18177
  * Show a Refresh button above the table (top-left) once rows are on screen.
17798
18178
  * Hidden during in-grid empty/error — those use Retry in the body. Host refetches
@@ -17924,6 +18304,26 @@ class TableComponent {
17924
18304
  /** Top-left Refresh — only when rows are on screen (empty/error use in-body Retry). */
17925
18305
  showToolbarRefresh = computed(() => this.showRefresh() && this.bodyKind() === 'rows', /* @ts-ignore */
17926
18306
  ...(ngDevMode ? [{ debugName: "showToolbarRefresh" }] : /* istanbul ignore next */ []));
18307
+ /**
18308
+ * Applies a shipping-mode segment selection through
18309
+ * {@link ShippingModeService.requestModeChange}.
18310
+ *
18311
+ * @param id - Segment id (`stn` / `sfn`) or null.
18312
+ */
18313
+ onShippingModeChange(id) {
18314
+ if (id !== 'stn' && id !== 'sfn') {
18315
+ return;
18316
+ }
18317
+ this.shippingModeActiveId.set(id);
18318
+ if (id === this.shipping.mode()) {
18319
+ return;
18320
+ }
18321
+ this.shipping.requestModeChange(id).subscribe((ok) => {
18322
+ if (!ok) {
18323
+ this.shippingModeActiveId.set(this.shipping.mode());
18324
+ }
18325
+ });
18326
+ }
17927
18327
  visibleRowIds = computed(() => this.rowList().map((row, index) => this.rowId(row, index)), /* @ts-ignore */
17928
18328
  ...(ngDevMode ? [{ debugName: "visibleRowIds" }] : /* istanbul ignore next */ []));
17929
18329
  someRowsExpanded = computed(() => {
@@ -17983,11 +18383,12 @@ class TableComponent {
17983
18383
  return 'border-b border-border dark:border-white/10';
17984
18384
  }
17985
18385
  /**
17986
- * Opaque mode soft fill for header cells (sticky-safe in dark mode).
17987
- * @returns Soft solid background utilities.
18386
+ * Opaque header fill (sticky-safe). Neutral chrome — not mode-accented.
18387
+ * Light `#f0f2f5` / dark elevated ink surface.
18388
+ * @returns Background utility classes.
17988
18389
  */
17989
18390
  headerSoftClass() {
17990
- return this.modeColor.classes().softSolid;
18391
+ return 'bg-[#f0f2f5] dark:bg-[#2a2c31]';
17991
18392
  }
17992
18393
  /**
17993
18394
  * Body row classes — row marker for hover styles, plus hide cell borders on
@@ -18058,7 +18459,7 @@ class TableComponent {
18058
18459
  ? 'border-b-[1.5px] '
18059
18460
  : 'border-b ';
18060
18461
  return (border +
18061
- 'border-border whitespace-normal wrap-break-word px-[1.125rem] py-4 font-medium text-neutral-600 dark:border-white/10 dark:text-neutral-400 ' +
18462
+ 'border-border whitespace-normal wrap-break-word px-[1.125rem] py-4 font-medium text-neutral-500 dark:border-white/10 dark:text-neutral-400 ' +
18062
18463
  this.headerSoftClass() +
18063
18464
  ' ' +
18064
18465
  this.stickySurfaceClass(col, 'head') +
@@ -18223,14 +18624,28 @@ class TableComponent {
18223
18624
  this.sortChange.emit({ key, direction });
18224
18625
  }
18225
18626
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: TableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18226
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: TableComponent, isStandalone: true, selector: "africanies-table", inputs: { expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, rowTrackBy: { classPropertyName: "rowTrackBy", publicName: "rowTrackBy", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, stickyHeader: { classPropertyName: "stickyHeader", publicName: "stickyHeader", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, showRefresh: { classPropertyName: "showRefresh", publicName: "showRefresh", isSignal: true, isRequired: false, transformFunction: null }, refreshLabel: { classPropertyName: "refreshLabel", publicName: "refreshLabel", isSignal: true, isRequired: false, transformFunction: null }, refreshing: { classPropertyName: "refreshing", publicName: "refreshing", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingLabel: { classPropertyName: "loadingLabel", publicName: "loadingLabel", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, showFilter: { classPropertyName: "showFilter", publicName: "showFilter", isSignal: true, isRequired: false, transformFunction: null }, filterLabel: { classPropertyName: "filterLabel", publicName: "filterLabel", isSignal: true, isRequired: false, transformFunction: null }, filterCount: { classPropertyName: "filterCount", publicName: "filterCount", isSignal: true, isRequired: false, transformFunction: null }, showExport: { classPropertyName: "showExport", publicName: "showExport", isSignal: true, isRequired: false, transformFunction: null }, exportLabel: { classPropertyName: "exportLabel", publicName: "exportLabel", isSignal: true, isRequired: false, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowExpandChange: "rowExpandChange", sortChange: "sortChange", refreshClick: "refreshClick", filterClick: "filterClick", exportClick: "exportClick", pageChange: "pageChange", sizeChange: "sizeChange" }, host: { classAttribute: "block w-full min-w-0" }, queries: [{ propertyName: "cellDefs", predicate: CellDefDirective, isSignal: true }, { propertyName: "headerCellDefs", predicate: HeaderCellDefDirective, isSignal: true }, { propertyName: "rowDetailDefs", predicate: RowDetailDefDirective, isSignal: true }], ngImport: i0, template: `
18627
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: TableComponent, isStandalone: true, selector: "africanies-table", inputs: { expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, rowTrackBy: { classPropertyName: "rowTrackBy", publicName: "rowTrackBy", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, stickyHeader: { classPropertyName: "stickyHeader", publicName: "stickyHeader", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, showShippingMode: { classPropertyName: "showShippingMode", publicName: "showShippingMode", isSignal: true, isRequired: false, transformFunction: null }, showRefresh: { classPropertyName: "showRefresh", publicName: "showRefresh", isSignal: true, isRequired: false, transformFunction: null }, refreshLabel: { classPropertyName: "refreshLabel", publicName: "refreshLabel", isSignal: true, isRequired: false, transformFunction: null }, refreshing: { classPropertyName: "refreshing", publicName: "refreshing", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingLabel: { classPropertyName: "loadingLabel", publicName: "loadingLabel", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, showFilter: { classPropertyName: "showFilter", publicName: "showFilter", isSignal: true, isRequired: false, transformFunction: null }, filterLabel: { classPropertyName: "filterLabel", publicName: "filterLabel", isSignal: true, isRequired: false, transformFunction: null }, filterCount: { classPropertyName: "filterCount", publicName: "filterCount", isSignal: true, isRequired: false, transformFunction: null }, showExport: { classPropertyName: "showExport", publicName: "showExport", isSignal: true, isRequired: false, transformFunction: null }, exportLabel: { classPropertyName: "exportLabel", publicName: "exportLabel", isSignal: true, isRequired: false, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowExpandChange: "rowExpandChange", sortChange: "sortChange", refreshClick: "refreshClick", filterClick: "filterClick", exportClick: "exportClick", pageChange: "pageChange", sizeChange: "sizeChange" }, host: { classAttribute: "block w-full min-w-0" }, queries: [{ propertyName: "cellDefs", predicate: CellDefDirective, isSignal: true }, { propertyName: "headerCellDefs", predicate: HeaderCellDefDirective, isSignal: true }, { propertyName: "rowDetailDefs", predicate: RowDetailDefDirective, isSignal: true }], viewQueries: [{ propertyName: "scrollport", first: true, predicate: ["scrollport"], descendants: true, isSignal: true }], ngImport: i0, template: `
18227
18628
  <div
18228
18629
  class="flex w-full min-w-0 flex-col gap-3"
18229
18630
  [attr.aria-busy]="loading() || refreshing() || null"
18230
18631
  >
18231
- @if (showRefresh() || showFilter() || showExport()) {
18232
- <div class="flex items-center justify-between gap-2">
18233
- <div class="flex items-center gap-2">
18632
+ @if (
18633
+ showShippingMode() ||
18634
+ showRefresh() ||
18635
+ showFilter() ||
18636
+ showExport()
18637
+ ) {
18638
+ <div class="flex flex-wrap items-center justify-between gap-2">
18639
+ <div class="flex min-w-0 flex-wrap items-center gap-2">
18640
+ @if (showShippingMode()) {
18641
+ <africanies-segment
18642
+ class="min-w-0"
18643
+ [items]="shippingModeItems"
18644
+ [activeId]="shippingModeActiveId()"
18645
+ ariaLabel="Shipping mode"
18646
+ (activeIdChange)="onShippingModeChange($event)"
18647
+ />
18648
+ }
18234
18649
  @if (showToolbarRefresh()) {
18235
18650
  <button
18236
18651
  africanies-button
@@ -18299,13 +18714,18 @@ class TableComponent {
18299
18714
  }
18300
18715
 
18301
18716
  <div
18302
- class="relative min-w-0 w-full overflow-x-auto rounded-panel border border-border bg-surface dark:border-white/10 dark:bg-ink-surface"
18303
- [class.lg:max-h-[min(70dvh,calc(100dvh-12rem))]]="stickyHeader()"
18304
- [class.lg:overflow-auto]="stickyHeader()"
18717
+ class="relative min-w-0 w-full overflow-hidden rounded-panel border border-border bg-surface dark:border-white/10 dark:bg-ink-surface"
18305
18718
  >
18306
- <table
18307
- class="w-max min-w-full table-auto border-separate border-spacing-0 bg-inherit text-left text-body text-ink dark:text-white"
18719
+ <div
18720
+ #scrollport
18721
+ class="africanies-table-scrollport min-w-0 w-full overflow-x-auto"
18722
+ [class.lg:max-h-[min(70dvh,calc(100dvh-12rem))]]="stickyHeader()"
18723
+ [class.lg:overflow-auto]="stickyHeader() && !paginationLoading()"
18724
+ [class.pointer-events-none]="paginationLoading()"
18308
18725
  >
18726
+ <table
18727
+ class="w-max min-w-full table-auto border-separate border-spacing-0 bg-inherit text-left text-body text-ink dark:text-white"
18728
+ >
18309
18729
  <thead [class]="headerRowClass()">
18310
18730
  <tr>
18311
18731
  @if (isExpandable()) {
@@ -18316,7 +18736,7 @@ class TableComponent {
18316
18736
  @if (someRowsExpanded()) {
18317
18737
  <button
18318
18738
  type="button"
18319
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18739
+ class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-500 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18320
18740
  aria-label="Collapse all"
18321
18741
  (click)="collapseAllRows()"
18322
18742
  >
@@ -18325,7 +18745,7 @@ class TableComponent {
18325
18745
  } @else {
18326
18746
  <button
18327
18747
  type="button"
18328
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus disabled:cursor-not-allowed disabled:opacity-40 dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18748
+ class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-500 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus disabled:cursor-not-allowed disabled:opacity-40 dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18329
18749
  aria-label="Expand all"
18330
18750
  [disabled]="rowList().length === 0"
18331
18751
  (click)="expandAllRows()"
@@ -18349,7 +18769,7 @@ class TableComponent {
18349
18769
  } @else if (col.sortable) {
18350
18770
  <button
18351
18771
  type="button"
18352
- class="inline-flex cursor-pointer items-center gap-1 rounded-sm font-medium text-neutral-600 hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:text-white"
18772
+ class="inline-flex cursor-pointer items-center gap-1 rounded-sm font-medium text-neutral-500 hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:text-white"
18353
18773
  (click)="onSortHeaderClick(col.key)"
18354
18774
  >
18355
18775
  <span>{{ col.header }}</span>
@@ -18445,41 +18865,52 @@ class TableComponent {
18445
18865
  [class.border-border]="isRowExpanded(row, i)"
18446
18866
  [class.dark:border-white/10]="isRowExpanded(row, i)"
18447
18867
  >
18448
- <td class="p-0 align-top" [attr.colspan]="columns().length + 1">
18868
+ <td class="p-0 align-top" [attr.colspan]="colSpan()">
18869
+ <!-- Sticky must sit outside overflow-hidden or it pins
18870
+ to the full table width and scrolls with the row. -->
18449
18871
  <div
18450
- class="grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none"
18451
- [style.grid-template-rows]="
18452
- isRowExpanded(row, i) ? '1fr' : '0fr'
18453
- "
18454
- [attr.aria-hidden]="!isRowExpanded(row, i)"
18455
- [class.pointer-events-none]="!isRowExpanded(row, i)"
18872
+ class="africanies-table-expand-panel bg-surface-sunken dark:bg-white/[0.05]"
18873
+ [style.width.px]="scrollportWidthPx() || null"
18874
+ [style.maxWidth.px]="scrollportWidthPx() || null"
18456
18875
  >
18457
18876
  <div
18458
- class="min-h-0 overflow-hidden border-t border-border-strong bg-surface-sunken dark:border-white/10 dark:bg-white/[0.05]"
18877
+ class="grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none"
18878
+ [style.grid-template-rows]="
18879
+ isRowExpanded(row, i) ? '1fr' : '0fr'
18880
+ "
18881
+ [attr.aria-hidden]="!isRowExpanded(row, i)"
18882
+ [class.pointer-events-none]="!isRowExpanded(row, i)"
18459
18883
  >
18460
- <dl
18461
- class="m-0 grid gap-x-6 gap-y-3 px-[1.125rem] py-4 transition-opacity duration-200 ease-out motion-reduce:transition-none sm:grid-cols-2 lg:grid-cols-3"
18462
- [class.opacity-0]="!isRowExpanded(row, i)"
18463
- [class.opacity-100]="isRowExpanded(row, i)"
18884
+ <div
18885
+ class="min-h-0 overflow-x-hidden overflow-y-hidden border-t border-border-strong dark:border-white/10"
18464
18886
  >
18465
- @for (detail of rowDetailDefs(); track detail.label()) {
18466
- <div class="min-w-0">
18467
- <dt
18468
- class="m-0 text-caption font-medium text-neutral-600 dark:text-neutral-400"
18469
- >
18470
- {{ detail.label() }}
18471
- </dt>
18472
- <dd
18473
- class="m-0 mt-0.5 text-body-sm text-ink dark:text-white"
18474
- >
18475
- <ng-container
18476
- [ngTemplateOutlet]="detail.template"
18477
- [ngTemplateOutletContext]="cellContext(row)"
18478
- />
18479
- </dd>
18480
- </div>
18481
- }
18482
- </dl>
18887
+ <dl
18888
+ class="m-0 grid grid-cols-[repeat(auto-fill,minmax(11rem,1fr))] gap-x-6 gap-y-3 overflow-x-hidden px-[1.125rem] py-4 transition-opacity duration-200 ease-out motion-reduce:transition-none"
18889
+ [class.opacity-0]="!isRowExpanded(row, i)"
18890
+ [class.opacity-100]="isRowExpanded(row, i)"
18891
+ >
18892
+ @for (
18893
+ detail of rowDetailDefs();
18894
+ track detail.label()
18895
+ ) {
18896
+ <div class="min-w-0">
18897
+ <dt
18898
+ class="m-0 text-caption font-medium text-neutral-600 dark:text-neutral-400"
18899
+ >
18900
+ {{ detail.label() }}
18901
+ </dt>
18902
+ <dd
18903
+ class="m-0 mt-0.5 break-words text-body-sm text-ink dark:text-white"
18904
+ >
18905
+ <ng-container
18906
+ [ngTemplateOutlet]="detail.template"
18907
+ [ngTemplateOutletContext]="cellContext(row)"
18908
+ />
18909
+ </dd>
18910
+ </div>
18911
+ }
18912
+ </dl>
18913
+ </div>
18483
18914
  </div>
18484
18915
  </div>
18485
18916
  </td>
@@ -18490,16 +18921,21 @@ class TableComponent {
18490
18921
  }
18491
18922
  </tbody>
18492
18923
  </table>
18924
+ </div>
18493
18925
  @if (paginationLoading()) {
18494
18926
  <div
18495
- class="pointer-events-none absolute inset-0 z-[5] flex items-start justify-center bg-surface/70 px-4 pt-16 dark:bg-ink-950/70"
18927
+ class="absolute inset-0 z-[5] bg-surface/70 dark:bg-ink-950/70"
18496
18928
  data-testid="africanies-table-keep-rows-loading"
18497
18929
  aria-hidden="true"
18498
18930
  >
18499
- <africanies-loading-state
18500
- mode="inline"
18501
- [message]="loadingLabel()"
18502
- />
18931
+ <div
18932
+ class="sticky top-0 flex h-[min(70dvh,100%)] items-center justify-center px-4"
18933
+ >
18934
+ <africanies-loading-state
18935
+ mode="inline"
18936
+ [message]="loadingLabel()"
18937
+ />
18938
+ </div>
18503
18939
  </div>
18504
18940
  }
18505
18941
  </div>
@@ -18514,7 +18950,7 @@ class TableComponent {
18514
18950
  />
18515
18951
  }
18516
18952
  </div>
18517
- `, isInline: true, styles: [":host tr.africanies-table-row:hover>td{background-color:#e9eef5}:host-context(.dark) tr.africanies-table-row:hover>td{background-color:#272729}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ButtonComponent, selector: "button[africanies-button], a[africanies-button]", inputs: ["variant", "size", "disabled", "loading"] }, { kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "component", type: EmptyStateComponent, selector: "africanies-empty-state", inputs: ["message", "refreshing"], outputs: ["retry"] }, { kind: "component", type: ErrorIndicatorComponent, selector: "africanies-error-indicator", inputs: ["error", "retryText", "refreshing", "refreshingText", "disabled"], outputs: ["retry"] }, { kind: "component", type: ErrorStateComponent, selector: "africanies-error-state", inputs: ["message", "refreshing"], outputs: ["retry"] }, { kind: "component", type: LoadingStateComponent, selector: "africanies-loading-state", inputs: ["message", "mode"] }, { kind: "component", type: PaginationComponent, selector: "africanies-pagination", inputs: ["meta", "disabled", "loading", "pageSizes"], outputs: ["pageChange", "sizeChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
18953
+ `, isInline: true, styles: [":host tr.africanies-table-row:hover>td{background-color:#e9eef5}:host-context(.dark) tr.africanies-table-row:hover>td{background-color:#272729}:host .africanies-table-scrollport{container-type:inline-size;container-name:africanies-table}:host .africanies-table-expand-panel{position:sticky;left:0;z-index:1;box-sizing:border-box;width:100cqi;max-width:100cqi;overflow-x:hidden}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ButtonComponent, selector: "button[africanies-button], a[africanies-button]", inputs: ["variant", "size", "disabled", "loading"] }, { kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "component", type: EmptyStateComponent, selector: "africanies-empty-state", inputs: ["message", "refreshing"], outputs: ["retry"] }, { kind: "component", type: ErrorIndicatorComponent, selector: "africanies-error-indicator", inputs: ["error", "retryText", "refreshing", "refreshingText", "disabled"], outputs: ["retry"] }, { kind: "component", type: ErrorStateComponent, selector: "africanies-error-state", inputs: ["message", "refreshing"], outputs: ["retry"] }, { kind: "component", type: LoadingStateComponent, selector: "africanies-loading-state", inputs: ["message", "mode"] }, { kind: "component", type: PaginationComponent, selector: "africanies-pagination", inputs: ["meta", "disabled", "loading", "pageSizes"], outputs: ["pageChange", "sizeChange"] }, { kind: "component", type: SegmentComponent, selector: "africanies-segment", inputs: ["items", "activeId", "ariaLabel", "linkActiveOptions", "keepScroll"], outputs: ["activeIdChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
18518
18954
  }
18519
18955
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: TableComponent, decorators: [{
18520
18956
  type: Component,
@@ -18527,6 +18963,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18527
18963
  ErrorStateComponent,
18528
18964
  LoadingStateComponent,
18529
18965
  PaginationComponent,
18966
+ SegmentComponent,
18530
18967
  ], host: {
18531
18968
  class: 'block w-full min-w-0',
18532
18969
  }, template: `
@@ -18534,9 +18971,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18534
18971
  class="flex w-full min-w-0 flex-col gap-3"
18535
18972
  [attr.aria-busy]="loading() || refreshing() || null"
18536
18973
  >
18537
- @if (showRefresh() || showFilter() || showExport()) {
18538
- <div class="flex items-center justify-between gap-2">
18539
- <div class="flex items-center gap-2">
18974
+ @if (
18975
+ showShippingMode() ||
18976
+ showRefresh() ||
18977
+ showFilter() ||
18978
+ showExport()
18979
+ ) {
18980
+ <div class="flex flex-wrap items-center justify-between gap-2">
18981
+ <div class="flex min-w-0 flex-wrap items-center gap-2">
18982
+ @if (showShippingMode()) {
18983
+ <africanies-segment
18984
+ class="min-w-0"
18985
+ [items]="shippingModeItems"
18986
+ [activeId]="shippingModeActiveId()"
18987
+ ariaLabel="Shipping mode"
18988
+ (activeIdChange)="onShippingModeChange($event)"
18989
+ />
18990
+ }
18540
18991
  @if (showToolbarRefresh()) {
18541
18992
  <button
18542
18993
  africanies-button
@@ -18605,13 +19056,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18605
19056
  }
18606
19057
 
18607
19058
  <div
18608
- class="relative min-w-0 w-full overflow-x-auto rounded-panel border border-border bg-surface dark:border-white/10 dark:bg-ink-surface"
18609
- [class.lg:max-h-[min(70dvh,calc(100dvh-12rem))]]="stickyHeader()"
18610
- [class.lg:overflow-auto]="stickyHeader()"
19059
+ class="relative min-w-0 w-full overflow-hidden rounded-panel border border-border bg-surface dark:border-white/10 dark:bg-ink-surface"
18611
19060
  >
18612
- <table
18613
- class="w-max min-w-full table-auto border-separate border-spacing-0 bg-inherit text-left text-body text-ink dark:text-white"
19061
+ <div
19062
+ #scrollport
19063
+ class="africanies-table-scrollport min-w-0 w-full overflow-x-auto"
19064
+ [class.lg:max-h-[min(70dvh,calc(100dvh-12rem))]]="stickyHeader()"
19065
+ [class.lg:overflow-auto]="stickyHeader() && !paginationLoading()"
19066
+ [class.pointer-events-none]="paginationLoading()"
18614
19067
  >
19068
+ <table
19069
+ class="w-max min-w-full table-auto border-separate border-spacing-0 bg-inherit text-left text-body text-ink dark:text-white"
19070
+ >
18615
19071
  <thead [class]="headerRowClass()">
18616
19072
  <tr>
18617
19073
  @if (isExpandable()) {
@@ -18622,7 +19078,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18622
19078
  @if (someRowsExpanded()) {
18623
19079
  <button
18624
19080
  type="button"
18625
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
19081
+ class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-500 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18626
19082
  aria-label="Collapse all"
18627
19083
  (click)="collapseAllRows()"
18628
19084
  >
@@ -18631,7 +19087,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18631
19087
  } @else {
18632
19088
  <button
18633
19089
  type="button"
18634
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus disabled:cursor-not-allowed disabled:opacity-40 dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
19090
+ class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-500 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus disabled:cursor-not-allowed disabled:opacity-40 dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18635
19091
  aria-label="Expand all"
18636
19092
  [disabled]="rowList().length === 0"
18637
19093
  (click)="expandAllRows()"
@@ -18655,7 +19111,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18655
19111
  } @else if (col.sortable) {
18656
19112
  <button
18657
19113
  type="button"
18658
- class="inline-flex cursor-pointer items-center gap-1 rounded-sm font-medium text-neutral-600 hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:text-white"
19114
+ class="inline-flex cursor-pointer items-center gap-1 rounded-sm font-medium text-neutral-500 hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:text-white"
18659
19115
  (click)="onSortHeaderClick(col.key)"
18660
19116
  >
18661
19117
  <span>{{ col.header }}</span>
@@ -18751,41 +19207,52 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18751
19207
  [class.border-border]="isRowExpanded(row, i)"
18752
19208
  [class.dark:border-white/10]="isRowExpanded(row, i)"
18753
19209
  >
18754
- <td class="p-0 align-top" [attr.colspan]="columns().length + 1">
19210
+ <td class="p-0 align-top" [attr.colspan]="colSpan()">
19211
+ <!-- Sticky must sit outside overflow-hidden or it pins
19212
+ to the full table width and scrolls with the row. -->
18755
19213
  <div
18756
- class="grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none"
18757
- [style.grid-template-rows]="
18758
- isRowExpanded(row, i) ? '1fr' : '0fr'
18759
- "
18760
- [attr.aria-hidden]="!isRowExpanded(row, i)"
18761
- [class.pointer-events-none]="!isRowExpanded(row, i)"
19214
+ class="africanies-table-expand-panel bg-surface-sunken dark:bg-white/[0.05]"
19215
+ [style.width.px]="scrollportWidthPx() || null"
19216
+ [style.maxWidth.px]="scrollportWidthPx() || null"
18762
19217
  >
18763
19218
  <div
18764
- class="min-h-0 overflow-hidden border-t border-border-strong bg-surface-sunken dark:border-white/10 dark:bg-white/[0.05]"
19219
+ class="grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none"
19220
+ [style.grid-template-rows]="
19221
+ isRowExpanded(row, i) ? '1fr' : '0fr'
19222
+ "
19223
+ [attr.aria-hidden]="!isRowExpanded(row, i)"
19224
+ [class.pointer-events-none]="!isRowExpanded(row, i)"
18765
19225
  >
18766
- <dl
18767
- class="m-0 grid gap-x-6 gap-y-3 px-[1.125rem] py-4 transition-opacity duration-200 ease-out motion-reduce:transition-none sm:grid-cols-2 lg:grid-cols-3"
18768
- [class.opacity-0]="!isRowExpanded(row, i)"
18769
- [class.opacity-100]="isRowExpanded(row, i)"
19226
+ <div
19227
+ class="min-h-0 overflow-x-hidden overflow-y-hidden border-t border-border-strong dark:border-white/10"
18770
19228
  >
18771
- @for (detail of rowDetailDefs(); track detail.label()) {
18772
- <div class="min-w-0">
18773
- <dt
18774
- class="m-0 text-caption font-medium text-neutral-600 dark:text-neutral-400"
18775
- >
18776
- {{ detail.label() }}
18777
- </dt>
18778
- <dd
18779
- class="m-0 mt-0.5 text-body-sm text-ink dark:text-white"
18780
- >
18781
- <ng-container
18782
- [ngTemplateOutlet]="detail.template"
18783
- [ngTemplateOutletContext]="cellContext(row)"
18784
- />
18785
- </dd>
18786
- </div>
18787
- }
18788
- </dl>
19229
+ <dl
19230
+ class="m-0 grid grid-cols-[repeat(auto-fill,minmax(11rem,1fr))] gap-x-6 gap-y-3 overflow-x-hidden px-[1.125rem] py-4 transition-opacity duration-200 ease-out motion-reduce:transition-none"
19231
+ [class.opacity-0]="!isRowExpanded(row, i)"
19232
+ [class.opacity-100]="isRowExpanded(row, i)"
19233
+ >
19234
+ @for (
19235
+ detail of rowDetailDefs();
19236
+ track detail.label()
19237
+ ) {
19238
+ <div class="min-w-0">
19239
+ <dt
19240
+ class="m-0 text-caption font-medium text-neutral-600 dark:text-neutral-400"
19241
+ >
19242
+ {{ detail.label() }}
19243
+ </dt>
19244
+ <dd
19245
+ class="m-0 mt-0.5 break-words text-body-sm text-ink dark:text-white"
19246
+ >
19247
+ <ng-container
19248
+ [ngTemplateOutlet]="detail.template"
19249
+ [ngTemplateOutletContext]="cellContext(row)"
19250
+ />
19251
+ </dd>
19252
+ </div>
19253
+ }
19254
+ </dl>
19255
+ </div>
18789
19256
  </div>
18790
19257
  </div>
18791
19258
  </td>
@@ -18796,16 +19263,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18796
19263
  }
18797
19264
  </tbody>
18798
19265
  </table>
19266
+ </div>
18799
19267
  @if (paginationLoading()) {
18800
19268
  <div
18801
- class="pointer-events-none absolute inset-0 z-[5] flex items-start justify-center bg-surface/70 px-4 pt-16 dark:bg-ink-950/70"
19269
+ class="absolute inset-0 z-[5] bg-surface/70 dark:bg-ink-950/70"
18802
19270
  data-testid="africanies-table-keep-rows-loading"
18803
19271
  aria-hidden="true"
18804
19272
  >
18805
- <africanies-loading-state
18806
- mode="inline"
18807
- [message]="loadingLabel()"
18808
- />
19273
+ <div
19274
+ class="sticky top-0 flex h-[min(70dvh,100%)] items-center justify-center px-4"
19275
+ >
19276
+ <africanies-loading-state
19277
+ mode="inline"
19278
+ [message]="loadingLabel()"
19279
+ />
19280
+ </div>
18809
19281
  </div>
18810
19282
  }
18811
19283
  </div>
@@ -18820,8 +19292,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18820
19292
  />
18821
19293
  }
18822
19294
  </div>
18823
- `, styles: [":host tr.africanies-table-row:hover>td{background-color:#e9eef5}:host-context(.dark) tr.africanies-table-row:hover>td{background-color:#272729}\n"] }]
18824
- }], propDecorators: { expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], rowTrackBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowTrackBy", required: false }] }], rowExpandChange: [{ type: i0.Output, args: ["rowExpandChange"] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], stickyHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "stickyHeader", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }], showRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRefresh", required: false }] }], refreshLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "refreshLabel", required: false }] }], refreshing: [{ type: i0.Input, args: [{ isSignal: true, alias: "refreshing", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], loadingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingLabel", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], showFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFilter", required: false }] }], filterLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterLabel", required: false }] }], filterCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterCount", required: false }] }], showExport: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExport", required: false }] }], exportLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportLabel", required: false }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], refreshClick: [{ type: i0.Output, args: ["refreshClick"] }], filterClick: [{ type: i0.Output, args: ["filterClick"] }], exportClick: [{ type: i0.Output, args: ["exportClick"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], sizeChange: [{ type: i0.Output, args: ["sizeChange"] }], cellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => CellDefDirective), { isSignal: true }] }], headerCellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => HeaderCellDefDirective), { isSignal: true }] }], rowDetailDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => RowDetailDefDirective), { isSignal: true }] }] } });
19295
+ `, styles: [":host tr.africanies-table-row:hover>td{background-color:#e9eef5}:host-context(.dark) tr.africanies-table-row:hover>td{background-color:#272729}:host .africanies-table-scrollport{container-type:inline-size;container-name:africanies-table}:host .africanies-table-expand-panel{position:sticky;left:0;z-index:1;box-sizing:border-box;width:100cqi;max-width:100cqi;overflow-x:hidden}\n"] }]
19296
+ }], ctorParameters: () => [], propDecorators: { scrollport: [{ type: i0.ViewChild, args: ['scrollport', { isSignal: true }] }], expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], rowTrackBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowTrackBy", required: false }] }], rowExpandChange: [{ type: i0.Output, args: ["rowExpandChange"] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], stickyHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "stickyHeader", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }], showShippingMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "showShippingMode", required: false }] }], showRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRefresh", required: false }] }], refreshLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "refreshLabel", required: false }] }], refreshing: [{ type: i0.Input, args: [{ isSignal: true, alias: "refreshing", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], loadingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingLabel", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], showFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFilter", required: false }] }], filterLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterLabel", required: false }] }], filterCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterCount", required: false }] }], showExport: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExport", required: false }] }], exportLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportLabel", required: false }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], refreshClick: [{ type: i0.Output, args: ["refreshClick"] }], filterClick: [{ type: i0.Output, args: ["filterClick"] }], exportClick: [{ type: i0.Output, args: ["exportClick"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], sizeChange: [{ type: i0.Output, args: ["sizeChange"] }], cellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => CellDefDirective), { isSignal: true }] }], headerCellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => HeaderCellDefDirective), { isSignal: true }] }], rowDetailDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => RowDetailDefDirective), { isSignal: true }] }] } });
18825
19297
 
18826
19298
  /**
18827
19299
  * Presentational table with template-based cells.
@@ -19405,6 +19877,7 @@ class AfricaniesNavigationModule {
19405
19877
  BreadcrumbComponent,
19406
19878
  TabsComponent,
19407
19879
  SegmentComponent,
19880
+ ShippingModeSwitchComponent,
19408
19881
  SideNavComponent] });
19409
19882
  }
19410
19883
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AfricaniesNavigationModule, decorators: [{