@africanies/angular-web-sdk 0.1.9 → 0.1.11

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 (70) hide show
  1. package/.nx/cache/1149535958171016935/dist/libs/africanies-theme/README.md +46 -0
  2. package/.nx/cache/1149535958171016935/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +243 -0
  3. package/.nx/cache/1149535958171016935/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -0
  4. package/.nx/cache/1149535958171016935/dist/libs/africanies-theme/package.json +35 -0
  5. package/.nx/cache/1149535958171016935/dist/libs/africanies-theme/tailwind-preset.cjs +330 -0
  6. package/.nx/cache/1149535958171016935/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +178 -0
  7. package/.nx/cache/14892813664845064572/dist/libs/africanies-ui/README.md +68 -0
  8. package/.nx/cache/14892813664845064572/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
  9. package/.nx/cache/14892813664845064572/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
  10. package/.nx/cache/14892813664845064572/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +5 -0
  11. package/.nx/cache/14892813664845064572/dist/libs/africanies-ui/assets/carriers/dhl.svg +7 -0
  12. package/.nx/cache/14892813664845064572/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +20013 -0
  13. package/.nx/cache/14892813664845064572/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -0
  14. package/.nx/cache/14892813664845064572/dist/libs/africanies-ui/package.json +35 -0
  15. package/.nx/cache/14892813664845064572/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +5617 -0
  16. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/README.md +68 -0
  17. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
  18. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
  19. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +5 -0
  20. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/assets/carriers/dhl.svg +7 -0
  21. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +19998 -0
  22. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -0
  23. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/package.json +35 -0
  24. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +5616 -0
  25. package/.nx/cache/run.json +18 -18
  26. package/.nx/cache/terminalOutputs/1149535958171016935 +21 -0
  27. package/.nx/cache/terminalOutputs/14892813664845064572 +21 -0
  28. package/.nx/cache/terminalOutputs/17909810599991457481 +40 -0
  29. package/.nx/cache/terminalOutputs/2927637324428060203 +42 -0
  30. package/.nx/cache/terminalOutputs/3735952477975556635 +21 -0
  31. package/.nx/cache/terminalOutputs/4787481949164239892 +8 -0
  32. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db +0 -0
  33. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-shm +0 -0
  34. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-wal +0 -0
  35. package/.nx/workspace-data/d/daemon.log +3807 -0
  36. package/.nx/workspace-data/d/server-process.json +2 -2
  37. package/.nx/workspace-data/file-map.json +3254 -3260
  38. package/.nx/workspace-data/nx_files.nxt +0 -0
  39. package/.nx/workspace-data/project-graph.json +2 -2
  40. package/README.md +0 -17
  41. package/apps/playground/src/app/pages/chip-page.ts +11 -1
  42. package/apps/playground/src/app/pages/tokens-page.ts +2 -2
  43. package/apps/playground/src/app/snippets/chip.snippets.ts +5 -0
  44. package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +8 -8
  45. package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -1
  46. package/dist/libs/africanies-theme/tailwind-preset.cjs +3 -3
  47. package/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +5 -3
  48. package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +986 -473
  49. package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -1
  50. package/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +135 -39
  51. package/libs/africanies-theme/src/lib/mode-color.safelist.ts +2 -2
  52. package/libs/africanies-theme/src/lib/mode-color.service.spec.ts +9 -7
  53. package/libs/africanies-theme/src/lib/mode-color.service.ts +9 -7
  54. package/libs/africanies-theme/tailwind-preset.cjs +3 -3
  55. package/libs/africanies-ui/src/lib/avatar/avatar-menu.component.ts +25 -21
  56. package/libs/africanies-ui/src/lib/button/button.component.spec.ts +13 -8
  57. package/libs/africanies-ui/src/lib/chip/chip.component.ts +74 -6
  58. package/libs/africanies-ui/src/lib/layout/app-shell-header.component.ts +35 -12
  59. package/libs/africanies-ui/src/lib/layout/header-greeting.util.ts +2 -0
  60. package/libs/africanies-ui/src/lib/layout/header-weather.spec.ts +120 -9
  61. package/libs/africanies-ui/src/lib/layout/header-weather.ts +79 -18
  62. package/libs/africanies-ui/src/lib/navigation/segment/segment.component.ts +98 -22
  63. package/libs/africanies-ui/src/lib/navigation/shipping-mode-switch/shipping-mode-switch.component.spec.ts +15 -0
  64. package/libs/africanies-ui/src/lib/navigation/shipping-mode-switch/shipping-mode-switch.component.ts +195 -64
  65. package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.html +4 -2
  66. package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.ts +3 -1
  67. package/libs/africanies-ui/src/lib/table/table.component.spec.ts +3 -0
  68. package/libs/africanies-ui/src/lib/table/table.component.ts +254 -109
  69. package/package.json +1 -1
  70. 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,144 @@ 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>
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
+ <!-- STN / SFN chip only when the tiles are collapsed — open panel already shows both modes. -->
13777
+ @if (!panelOpen()) {
13778
+ <africanies-chip [variant]="modeChipVariant()" size="sm">
13779
+ {{ modeChipLabel() }}
13780
+ </africanies-chip>
13647
13781
  }
13648
- </button>
13649
- </div>
13650
- `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
13782
+ <span class="min-w-0 flex-1" aria-hidden="true"></span>
13783
+ <button
13784
+ type="button"
13785
+ 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"
13786
+ [attr.aria-label]="
13787
+ panelOpen() ? 'Hide shipping modes' : 'Show shipping modes'
13788
+ "
13789
+ [attr.aria-expanded]="panelOpen()"
13790
+ (click)="togglePanel()"
13791
+ >
13792
+ <africanies-icon
13793
+ [name]="panelOpen() ? 'chevron-up' : 'chevron-down'"
13794
+ [size]="14"
13795
+ />
13796
+ </button>
13797
+ </div>
13798
+
13799
+ <div
13800
+ class="grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none"
13801
+ [style.grid-template-rows]="panelOpen() ? '1fr' : '0fr'"
13802
+ [attr.aria-hidden]="!panelOpen()"
13803
+ [class.pointer-events-none]="!panelOpen()"
13804
+ >
13805
+ <div class="min-h-0 overflow-hidden">
13806
+ <div
13807
+ class="grid grid-cols-2 gap-1.5"
13808
+ role="radiogroup"
13809
+ aria-label="Shipping mode"
13810
+ >
13811
+ <button
13812
+ type="button"
13813
+ role="radio"
13814
+ [attr.aria-checked]="mode() === 'stn'"
13815
+ aria-label="Shipping to Nigeria"
13816
+ [class]="cardClass('stn')"
13817
+ (click)="select('stn')"
13818
+ >
13819
+ <svg
13820
+ viewBox="0 0 122.88 107.54"
13821
+ class="h-6 w-6 shrink-0 rotate-180"
13822
+ aria-hidden="true"
13823
+ focusable="false"
13824
+ >
13825
+ <path [attr.d]="glyphPath()" fill="currentColor" />
13826
+ </svg>
13827
+ <span class="flex flex-col leading-tight">
13828
+ <span>Shipping</span>
13829
+ <span>to Nigeria</span>
13830
+ </span>
13831
+ </button>
13832
+ <button
13833
+ type="button"
13834
+ role="radio"
13835
+ [attr.aria-checked]="mode() === 'sfn'"
13836
+ aria-label="Shipping from Nigeria"
13837
+ [class]="cardClass('sfn')"
13838
+ (click)="select('sfn')"
13839
+ >
13840
+ <svg
13841
+ viewBox="0 0 122.88 107.54"
13842
+ class="h-6 w-6 shrink-0"
13843
+ aria-hidden="true"
13844
+ focusable="false"
13845
+ >
13846
+ <path [attr.d]="glyphPath()" fill="currentColor" />
13847
+ </svg>
13848
+ <span class="flex flex-col leading-tight">
13849
+ <span>Shipping</span>
13850
+ <span>from Nigeria</span>
13851
+ </span>
13852
+ </button>
13853
+ </div>
13854
+ </div>
13855
+ </div>
13856
+ }
13857
+ `, 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
13858
  }
13652
13859
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ShippingModeSwitchComponent, decorators: [{
13653
13860
  type: Component,
@@ -13655,74 +13862,146 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
13655
13862
  selector: 'africanies-shipping-mode-switch',
13656
13863
  standalone: true,
13657
13864
  changeDetection: ChangeDetectionStrategy.OnPush,
13865
+ imports: [AfricaniesIconComponent, ChipComponent],
13658
13866
  host: {
13659
13867
  '[class]': 'hostClass()',
13660
13868
  },
13661
13869
  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')"
13870
+ @if (collapsed()) {
13871
+ <!-- Narrow rail: icon tiles only — chip + chevron reads as cramped. -->
13872
+ <div
13873
+ class="grid grid-cols-1 gap-1.5"
13874
+ role="radiogroup"
13875
+ aria-label="Shipping mode"
13683
13876
  >
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"
13877
+ <button
13878
+ type="button"
13879
+ role="radio"
13880
+ [attr.aria-checked]="mode() === 'stn'"
13881
+ aria-label="Shipping to Nigeria"
13882
+ title="Shipping to Nigeria"
13883
+ [class]="cardClass('stn')"
13884
+ (click)="select('stn')"
13689
13885
  >
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"
13886
+ <svg
13887
+ viewBox="0 0 122.88 107.54"
13888
+ class="h-5 w-5 shrink-0 rotate-180"
13889
+ aria-hidden="true"
13890
+ focusable="false"
13891
+ >
13892
+ <path [attr.d]="glyphPath()" fill="currentColor" />
13893
+ </svg>
13894
+ </button>
13895
+ <button
13896
+ type="button"
13897
+ role="radio"
13898
+ [attr.aria-checked]="mode() === 'sfn'"
13899
+ aria-label="Shipping from Nigeria"
13900
+ title="Shipping from Nigeria"
13901
+ [class]="cardClass('sfn')"
13902
+ (click)="select('sfn')"
13712
13903
  >
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>
13904
+ <svg
13905
+ viewBox="0 0 122.88 107.54"
13906
+ class="h-5 w-5 shrink-0"
13907
+ aria-hidden="true"
13908
+ focusable="false"
13909
+ >
13910
+ <path [attr.d]="glyphPath()" fill="currentColor" />
13911
+ </svg>
13912
+ </button>
13913
+ </div>
13914
+ } @else {
13915
+ <div class="mb-2 flex items-center gap-1.5">
13916
+ <p
13917
+ class="m-0 shrink-0 px-1 text-caption font-semibold uppercase tracking-wide text-neutral-600 dark:text-neutral-400"
13918
+ >
13919
+ Shipping mode
13920
+ </p>
13921
+ <!-- STN / SFN chip only when the tiles are collapsed — open panel already shows both modes. -->
13922
+ @if (!panelOpen()) {
13923
+ <africanies-chip [variant]="modeChipVariant()" size="sm">
13924
+ {{ modeChipLabel() }}
13925
+ </africanies-chip>
13720
13926
  }
13721
- </button>
13722
- </div>
13927
+ <span class="min-w-0 flex-1" aria-hidden="true"></span>
13928
+ <button
13929
+ type="button"
13930
+ 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"
13931
+ [attr.aria-label]="
13932
+ panelOpen() ? 'Hide shipping modes' : 'Show shipping modes'
13933
+ "
13934
+ [attr.aria-expanded]="panelOpen()"
13935
+ (click)="togglePanel()"
13936
+ >
13937
+ <africanies-icon
13938
+ [name]="panelOpen() ? 'chevron-up' : 'chevron-down'"
13939
+ [size]="14"
13940
+ />
13941
+ </button>
13942
+ </div>
13943
+
13944
+ <div
13945
+ class="grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none"
13946
+ [style.grid-template-rows]="panelOpen() ? '1fr' : '0fr'"
13947
+ [attr.aria-hidden]="!panelOpen()"
13948
+ [class.pointer-events-none]="!panelOpen()"
13949
+ >
13950
+ <div class="min-h-0 overflow-hidden">
13951
+ <div
13952
+ class="grid grid-cols-2 gap-1.5"
13953
+ role="radiogroup"
13954
+ aria-label="Shipping mode"
13955
+ >
13956
+ <button
13957
+ type="button"
13958
+ role="radio"
13959
+ [attr.aria-checked]="mode() === 'stn'"
13960
+ aria-label="Shipping to Nigeria"
13961
+ [class]="cardClass('stn')"
13962
+ (click)="select('stn')"
13963
+ >
13964
+ <svg
13965
+ viewBox="0 0 122.88 107.54"
13966
+ class="h-6 w-6 shrink-0 rotate-180"
13967
+ aria-hidden="true"
13968
+ focusable="false"
13969
+ >
13970
+ <path [attr.d]="glyphPath()" fill="currentColor" />
13971
+ </svg>
13972
+ <span class="flex flex-col leading-tight">
13973
+ <span>Shipping</span>
13974
+ <span>to Nigeria</span>
13975
+ </span>
13976
+ </button>
13977
+ <button
13978
+ type="button"
13979
+ role="radio"
13980
+ [attr.aria-checked]="mode() === 'sfn'"
13981
+ aria-label="Shipping from Nigeria"
13982
+ [class]="cardClass('sfn')"
13983
+ (click)="select('sfn')"
13984
+ >
13985
+ <svg
13986
+ viewBox="0 0 122.88 107.54"
13987
+ class="h-6 w-6 shrink-0"
13988
+ aria-hidden="true"
13989
+ focusable="false"
13990
+ >
13991
+ <path [attr.d]="glyphPath()" fill="currentColor" />
13992
+ </svg>
13993
+ <span class="flex flex-col leading-tight">
13994
+ <span>Shipping</span>
13995
+ <span>from Nigeria</span>
13996
+ </span>
13997
+ </button>
13998
+ </div>
13999
+ </div>
14000
+ </div>
14001
+ }
13723
14002
  `,
13724
14003
  }]
13725
- }], propDecorators: { collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }] } });
14004
+ }], 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
14005
 
13727
14006
  /**
13728
14007
  * Side-nav shipping mode switcher (`africanies-shipping-mode-switch`).
@@ -13869,7 +14148,9 @@ const DEFAULT_LINK_ACTIVE$1 = {
13869
14148
  * **Expanded rail:** branches start open by default (`expandBranchesByDefault`).
13870
14149
  * Clicking a parent only opens/closes the branch (no route change). When any
13871
14150
  * 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.
14151
+ * The rail expand/collapse control stays pinned in the logo header. Project
14152
+ * chrome above the scrollable list with `pre-nav` (e.g. shipping modes) or
14153
+ * below with `footer`.
13873
14154
  * **Collapsed rail:** clicking a parent expands the rail, opens the
13874
14155
  * branch, and activates the first enabled child (router or `activeId`).
13875
14156
  *
@@ -14185,13 +14466,13 @@ class SideNavComponent {
14185
14466
  return null;
14186
14467
  }
14187
14468
  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 });
14469
+ 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
14470
  }
14190
14471
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SideNavComponent, decorators: [{
14191
14472
  type: Component,
14192
14473
  args: [{ selector: 'africanies-side-nav', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AfricaniesIconComponent, RouterLink, NgTemplateOutlet], host: {
14193
14474
  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"] }]
14475
+ }, 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
14476
  }], 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
14477
 
14197
14478
  /**
@@ -14931,28 +15212,32 @@ class AvatarMenuComponent {
14931
15212
  showCaret = input(true, { ...(ngDevMode ? { debugName: "showCaret" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
14932
15213
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AvatarMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14933
15214
  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()"
15215
+ @if (menuItems().length > 0) {
15216
+ <africanies-action-menu
15217
+ [items]="menuItems()"
15218
+ [ariaLabel]="ariaLabel()"
15219
+ [disabled]="disabled()"
14944
15220
  >
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>
15221
+ <button
15222
+ type="button"
15223
+ africaniesActionMenuTrigger
15224
+ 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"
15225
+ [attr.aria-label]="ariaLabel()"
15226
+ >
15227
+ <africanies-avatar [name]="name()" [src]="src()" [size]="size()" />
15228
+ @if (showCaret()) {
15229
+ <africanies-icon
15230
+ name="chevron-down"
15231
+ [size]="14"
15232
+ class="shrink-0 text-neutral-600 dark:text-neutral-400"
15233
+ aria-hidden="true"
15234
+ />
15235
+ }
15236
+ </button>
15237
+ </africanies-action-menu>
15238
+ } @else {
15239
+ <africanies-avatar [name]="name()" [src]="src()" [size]="size()" />
15240
+ }
14956
15241
  `, 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
15242
  }
14958
15243
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AvatarMenuComponent, decorators: [{
@@ -14968,28 +15253,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
14968
15253
  AfricaniesIconComponent,
14969
15254
  ],
14970
15255
  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()"
15256
+ @if (menuItems().length > 0) {
15257
+ <africanies-action-menu
15258
+ [items]="menuItems()"
15259
+ [ariaLabel]="ariaLabel()"
15260
+ [disabled]="disabled()"
14981
15261
  >
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>
15262
+ <button
15263
+ type="button"
15264
+ africaniesActionMenuTrigger
15265
+ 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"
15266
+ [attr.aria-label]="ariaLabel()"
15267
+ >
15268
+ <africanies-avatar [name]="name()" [src]="src()" [size]="size()" />
15269
+ @if (showCaret()) {
15270
+ <africanies-icon
15271
+ name="chevron-down"
15272
+ [size]="14"
15273
+ class="shrink-0 text-neutral-600 dark:text-neutral-400"
15274
+ aria-hidden="true"
15275
+ />
15276
+ }
15277
+ </button>
15278
+ </africanies-action-menu>
15279
+ } @else {
15280
+ <africanies-avatar [name]="name()" [src]="src()" [size]="size()" />
15281
+ }
14993
15282
  `,
14994
15283
  }]
14995
15284
  }], 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 }] }] } });
@@ -16091,12 +16380,14 @@ function hashString(value) {
16091
16380
  return hash;
16092
16381
  }
16093
16382
 
16094
- /** Bump when cache shape or lookup strategy changes. */
16095
- const CACHE_KEY = 'africanies-header-weather-v5';
16383
+ /** Bump when cache shape or city resolution strategy changes. */
16384
+ const CACHE_KEY = 'africanies-header-weather-v7';
16096
16385
  const FETCH_MS = 4_000;
16097
16386
  const BROWSER_GEO_MS = 3_500;
16098
- /** IP fallback for coordinates only city strings are often wrong. */
16387
+ /** IP fallback city string is used only when reverse-geocode is empty. */
16099
16388
  const GEO_URL = 'https://get.geojs.io/v1/ip/geo.json';
16389
+ /** Client reverse-geocode so the label matches the forecast coordinates. */
16390
+ const REVERSE_GEO_URL = 'https://api.bigdatacloud.net/data/reverse-geocode-client';
16100
16391
  const WEATHER_LABELS = {
16101
16392
  clear: 'Clear',
16102
16393
  cloudy: 'Cloudy',
@@ -16166,12 +16457,11 @@ function headerWeatherIcon(kind, hour) {
16166
16457
  return 'cloud-o';
16167
16458
  }
16168
16459
  /**
16169
- * Local forecast via browser / IP coordinates + Open-Meteo (no API key).
16460
+ * City-level forecast via browser / IP geolocation + Open-Meteo (no API key).
16170
16461
  *
16171
- * Coordinates prefer the browser Geolocation API (may prompt once).
16172
- * Otherwise IP geo is used for lat/lon only — city labels are not shown
16173
- * because IP and reverse-geocode cities are often wrong (e.g. NG IPs
16174
- * labelled Lagos).
16462
+ * Coordinates prefer the browser Geolocation API (accurate for travellers;
16463
+ * may prompt once). Otherwise IP geo is used. The city label is reverse-
16464
+ * geocoded from those coordinates, with the IP city as a fallback.
16175
16465
  *
16176
16466
  * Fails closed: missing browser APIs, timeouts, and HTTP errors all return
16177
16467
  * `null` so the greeting can stay time-of-day only. Cached per local hour
@@ -16208,8 +16498,10 @@ async function loadHeaderWeather(fetchFn) {
16208
16498
  return null;
16209
16499
  }
16210
16500
  const temperatureC = asNumber(forecast?.current?.temperature_2m) ?? undefined;
16211
- const weather = { kind, temperatureC };
16212
- writeCache({ hour, kind, temperatureC });
16501
+ const city = (await reverseGeocodeCity(run, coords.latitude, coords.longitude)) ??
16502
+ coords.fallbackCity;
16503
+ const weather = { kind, temperatureC, city };
16504
+ writeCache({ hour, kind, temperatureC, city });
16213
16505
  return weather;
16214
16506
  }
16215
16507
  catch {
@@ -16232,9 +16524,16 @@ function readCache(hour) {
16232
16524
  if (parsed.hour !== hour || !parsed.kind) {
16233
16525
  return null;
16234
16526
  }
16527
+ // City-less entries (e.g. browser geo + failed reverse) used to stick for
16528
+ // the hour — treat them as a miss so we can recover the location label.
16529
+ const city = asCity(parsed.city);
16530
+ if (!city) {
16531
+ return null;
16532
+ }
16235
16533
  return {
16236
16534
  kind: parsed.kind,
16237
16535
  temperatureC: parsed.temperatureC,
16536
+ city,
16238
16537
  };
16239
16538
  }
16240
16539
  catch {
@@ -16251,20 +16550,31 @@ function writeCache(value) {
16251
16550
  }
16252
16551
  /**
16253
16552
  * Prefer device coordinates when available (may prompt once); otherwise fall
16254
- * back to IP lat/lon for the forecast only.
16553
+ * back to IP geo. IP is always fetched in parallel so its city can backfill
16554
+ * the label when reverse-geocode is empty (browser geo has no city string).
16255
16555
  */
16256
16556
  async function resolveCoordinates(fetchFn) {
16257
- const browser = await tryBrowserGeolocation();
16557
+ const [browser, ipGeo] = await Promise.all([
16558
+ tryBrowserGeolocation(),
16559
+ fetchIpGeo(fetchFn),
16560
+ ]);
16561
+ const ipCity = asCity(ipGeo?.city);
16258
16562
  if (browser) {
16259
- return browser;
16563
+ return { ...browser, fallbackCity: ipCity };
16260
16564
  }
16261
- const geo = (await fetchJson(fetchFn, GEO_URL));
16262
- const latitude = asNumber(geo?.latitude);
16263
- const longitude = asNumber(geo?.longitude);
16565
+ const latitude = asNumber(ipGeo?.latitude);
16566
+ const longitude = asNumber(ipGeo?.longitude);
16264
16567
  if (latitude === null || longitude === null) {
16265
16568
  return null;
16266
16569
  }
16267
- return { latitude, longitude };
16570
+ return {
16571
+ latitude,
16572
+ longitude,
16573
+ fallbackCity: ipCity,
16574
+ };
16575
+ }
16576
+ async function fetchIpGeo(fetchFn) {
16577
+ return (await fetchJson(fetchFn, GEO_URL));
16268
16578
  }
16269
16579
  async function tryBrowserGeolocation() {
16270
16580
  if (typeof navigator === 'undefined' ||
@@ -16289,6 +16599,15 @@ async function tryBrowserGeolocation() {
16289
16599
  });
16290
16600
  });
16291
16601
  }
16602
+ async function reverseGeocodeCity(fetchFn, latitude, longitude) {
16603
+ const url = `${REVERSE_GEO_URL}?latitude=${encodeURIComponent(String(latitude))}` +
16604
+ `&longitude=${encodeURIComponent(String(longitude))}` +
16605
+ `&localityLanguage=en`;
16606
+ const payload = (await fetchJson(fetchFn, url));
16607
+ return (asCity(payload?.city) ??
16608
+ asCity(payload?.locality) ??
16609
+ asCity(payload?.principalSubdivision));
16610
+ }
16292
16611
  function asNumber(value) {
16293
16612
  if (typeof value === 'number' && Number.isFinite(value)) {
16294
16613
  return value;
@@ -16299,6 +16618,16 @@ function asNumber(value) {
16299
16618
  }
16300
16619
  return null;
16301
16620
  }
16621
+ function asCity(value) {
16622
+ if (typeof value !== 'string') {
16623
+ return undefined;
16624
+ }
16625
+ const trimmed = value.trim();
16626
+ if (!trimmed || trimmed.toLowerCase() === 'n/a') {
16627
+ return undefined;
16628
+ }
16629
+ return trimmed;
16630
+ }
16302
16631
  async function fetchJson(fetchFn, url) {
16303
16632
  const controller = new AbortController();
16304
16633
  const timer = setTimeout(() => controller.abort(), FETCH_MS);
@@ -16442,6 +16771,11 @@ class AppShellHeaderComponent {
16442
16771
  return headerWeatherLabel(forecast.kind, this.now().getHours());
16443
16772
  }, /* @ts-ignore */
16444
16773
  ...(ngDevMode ? [{ debugName: "weatherCondition" }] : /* istanbul ignore next */ []));
16774
+ weatherCity = computed(() => {
16775
+ const city = this.weather()?.city?.trim();
16776
+ return city || null;
16777
+ }, /* @ts-ignore */
16778
+ ...(ngDevMode ? [{ debugName: "weatherCity" }] : /* istanbul ignore next */ []));
16445
16779
  weatherAriaLabel = computed(() => {
16446
16780
  const forecast = this.weather();
16447
16781
  if (!forecast) {
@@ -16449,9 +16783,11 @@ class AppShellHeaderComponent {
16449
16783
  }
16450
16784
  const condition = this.weatherCondition().toLowerCase();
16451
16785
  const temp = this.weatherTemp();
16452
- return temp === null
16453
- ? `Weather: ${condition}`
16454
- : `Weather: ${temp} degrees Celsius, ${condition}`;
16786
+ const city = this.weatherCity();
16787
+ const degrees = temp === null
16788
+ ? condition
16789
+ : `${temp} degrees Celsius, ${condition}`;
16790
+ return city ? `Weather in ${city}: ${degrees}` : `Weather: ${degrees}`;
16455
16791
  }, /* @ts-ignore */
16456
16792
  ...(ngDevMode ? [{ debugName: "weatherAriaLabel" }] : /* istanbul ignore next */ []));
16457
16793
  unreadCount = computed(() => (this.notifications() ?? []).filter((n) => !n.read).length, /* @ts-ignore */
@@ -16558,7 +16894,9 @@ class AppShellHeaderComponent {
16558
16894
  <span
16559
16895
  class="hidden text-caption text-neutral-600 md:inline dark:text-neutral-400"
16560
16896
  >
16561
- {{ weatherCondition() }}
16897
+ {{ weatherCondition() }}@if (weatherCity(); as city) {
16898
+ · {{ city }}
16899
+ }
16562
16900
  </span>
16563
16901
  </div>
16564
16902
  }
@@ -16617,18 +16955,26 @@ class AppShellHeaderComponent {
16617
16955
  }
16618
16956
 
16619
16957
  @if (userName()) {
16620
- <africanies-avatar-menu
16621
- [name]="userName()!"
16622
- [src]="userAvatarSrc()"
16623
- [menuItems]="userMenuItems()"
16624
- [size]="'md'"
16625
- />
16958
+ @if (userMenuItems().length > 0) {
16959
+ <africanies-avatar-menu
16960
+ [name]="userName()!"
16961
+ [src]="userAvatarSrc()"
16962
+ [menuItems]="userMenuItems()"
16963
+ [size]="'md'"
16964
+ />
16965
+ } @else {
16966
+ <africanies-avatar
16967
+ [name]="userName()!"
16968
+ [src]="userAvatarSrc()"
16969
+ size="md"
16970
+ />
16971
+ }
16626
16972
  }
16627
16973
  </div>
16628
16974
  }
16629
16975
  </div>
16630
16976
  </div>
16631
- `, 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 });
16977
+ `, 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 });
16632
16978
  }
16633
16979
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AppShellHeaderComponent, decorators: [{
16634
16980
  type: Component,
@@ -16636,7 +16982,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16636
16982
  selector: 'africanies-app-shell-header',
16637
16983
  standalone: true,
16638
16984
  changeDetection: ChangeDetectionStrategy.OnPush,
16639
- imports: [AfricaniesIconComponent, AvatarMenuComponent, DatePipe],
16985
+ imports: [
16986
+ AfricaniesIconComponent,
16987
+ AvatarComponent,
16988
+ AvatarMenuComponent,
16989
+ DatePipe,
16990
+ ],
16640
16991
  template: `
16641
16992
  <div
16642
16993
  class="flex min-w-0 items-center gap-2 px-3 py-2.5 sm:gap-4 sm:px-6 sm:py-3"
@@ -16685,7 +17036,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16685
17036
  <span
16686
17037
  class="hidden text-caption text-neutral-600 md:inline dark:text-neutral-400"
16687
17038
  >
16688
- {{ weatherCondition() }}
17039
+ {{ weatherCondition() }}@if (weatherCity(); as city) {
17040
+ · {{ city }}
17041
+ }
16689
17042
  </span>
16690
17043
  </div>
16691
17044
  }
@@ -16744,12 +17097,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
16744
17097
  }
16745
17098
 
16746
17099
  @if (userName()) {
16747
- <africanies-avatar-menu
16748
- [name]="userName()!"
16749
- [src]="userAvatarSrc()"
16750
- [menuItems]="userMenuItems()"
16751
- [size]="'md'"
16752
- />
17100
+ @if (userMenuItems().length > 0) {
17101
+ <africanies-avatar-menu
17102
+ [name]="userName()!"
17103
+ [src]="userAvatarSrc()"
17104
+ [menuItems]="userMenuItems()"
17105
+ [size]="'md'"
17106
+ />
17107
+ } @else {
17108
+ <africanies-avatar
17109
+ [name]="userName()!"
17110
+ [src]="userAvatarSrc()"
17111
+ size="md"
17112
+ />
17113
+ }
16753
17114
  }
16754
17115
  </div>
16755
17116
  }
@@ -17659,8 +18020,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17659
18020
  * `<ng-template africaniesCellDef="key">` templates. Columns without a matching
17660
18021
  * template fall back to rendering `row[key]` as plain text.
17661
18022
  *
17662
- * Optional toolbar: top-left **Refresh** (`showRefresh` {@link refreshClick}).
17663
- * Hidden while the body shows empty or error those states expose Retry instead.
18023
+ * Optional toolbar: top-left **shipping mode** segment on its own row above
18024
+ * Refresh / Filter / Export (on by default; `[showShippingMode]="false"` to
18025
+ * hide) and **Refresh** (`showRefresh` →
18026
+ * {@link refreshClick}). Refresh is hidden while
18027
+ * the body shows empty or error — those states expose Retry instead.
17664
18028
  * While {@link refreshing} is true the rows stay on screen — the refresh icon
17665
18029
  * spins (do not swap to a blocking loader). Use {@link loading} when the page
17666
18030
  * of data is changing (pagination / size): rows stay on screen, a keep-rows
@@ -17689,8 +18053,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17689
18053
  * and never reorder local rows.
17690
18054
  *
17691
18055
  * **Expandable rows:** project `<ng-template africaniesRowDetail="Label" let-row>`
17692
- * templates. A leading chevron column appears; the expanded panel renders each
17693
- * label with its template value in a responsive grid.
18056
+ * templates. A leading chevron column appears; the expanded panel sticks to the
18057
+ * scrollport width (so it does not scroll horizontally with the columns) and
18058
+ * lays out label/value pairs in a wrapping auto-fill grid.
17694
18059
  *
17695
18060
  * @typeParam T - Row record shape.
17696
18061
  *
@@ -17733,7 +18098,47 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
17733
18098
  * page scroll is unchanged.
17734
18099
  */
17735
18100
  class TableComponent {
17736
- modeColor = inject(ModeColorService);
18101
+ destroyRef = inject(DestroyRef);
18102
+ shipping = inject(ShippingModeService);
18103
+ /** Local STN / SFN segment options for {@link showShippingMode}. */
18104
+ shippingModeItems = [
18105
+ { id: 'stn', label: 'Shipping to Nigeria' },
18106
+ { id: 'sfn', label: 'Shipping from Nigeria' },
18107
+ ];
18108
+ /**
18109
+ * Segment selection mirror of {@link ShippingModeService.mode}. Kept local
18110
+ * so a denied {@link ShippingModeService.requestModeChange} can snap the
18111
+ * pills back when the service mode did not change.
18112
+ */
18113
+ shippingModeActiveId = signal('sfn', /* @ts-ignore */
18114
+ ...(ngDevMode ? [{ debugName: "shippingModeActiveId" }] : /* istanbul ignore next */ []));
18115
+ /** Scrollport that owns horizontal overflow for the main grid. */
18116
+ scrollport = viewChild('scrollport', /* @ts-ignore */
18117
+ ...(ngDevMode ? [{ debugName: "scrollport" }] : /* istanbul ignore next */ []));
18118
+ /**
18119
+ * Visible width of {@link scrollport}. Applied to the expand panel so
18120
+ * details stay within the viewport and do not scroll with the columns.
18121
+ */
18122
+ scrollportWidthPx = signal(0, /* @ts-ignore */
18123
+ ...(ngDevMode ? [{ debugName: "scrollportWidthPx" }] : /* istanbul ignore next */ []));
18124
+ constructor() {
18125
+ effect(() => {
18126
+ this.shippingModeActiveId.set(this.shipping.mode());
18127
+ });
18128
+ afterNextRender(() => {
18129
+ const el = this.scrollport()?.nativeElement;
18130
+ if (!el) {
18131
+ return;
18132
+ }
18133
+ const sync = () => {
18134
+ this.scrollportWidthPx.set(el.clientWidth);
18135
+ };
18136
+ sync();
18137
+ const observer = new ResizeObserver(sync);
18138
+ observer.observe(el);
18139
+ this.destroyRef.onDestroy(() => observer.disconnect());
18140
+ });
18141
+ }
17737
18142
  /**
17738
18143
  * When false, hides row expansion even if {@link RowDetailDefDirective}
17739
18144
  * templates are projected. Defaults to true.
@@ -17768,6 +18173,13 @@ class TableComponent {
17768
18173
  */
17769
18174
  sort = input(null, /* @ts-ignore */
17770
18175
  ...(ngDevMode ? [{ debugName: "sort" }] : /* istanbul ignore next */ []));
18176
+ /**
18177
+ * Show the Shipping to / from Nigeria segment above the table (top-left).
18178
+ * On by default; set `[showShippingMode]="false"` to hide (e.g. modals).
18179
+ * Writes through {@link ShippingModeService.requestModeChange} so feature
18180
+ * guards still run.
18181
+ */
18182
+ showShippingMode = input(true, { ...(ngDevMode ? { debugName: "showShippingMode" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
17771
18183
  /**
17772
18184
  * Show a Refresh button above the table (top-left) once rows are on screen.
17773
18185
  * Hidden during in-grid empty/error — those use Retry in the body. Host refetches
@@ -17899,6 +18311,26 @@ class TableComponent {
17899
18311
  /** Top-left Refresh — only when rows are on screen (empty/error use in-body Retry). */
17900
18312
  showToolbarRefresh = computed(() => this.showRefresh() && this.bodyKind() === 'rows', /* @ts-ignore */
17901
18313
  ...(ngDevMode ? [{ debugName: "showToolbarRefresh" }] : /* istanbul ignore next */ []));
18314
+ /**
18315
+ * Applies a shipping-mode segment selection through
18316
+ * {@link ShippingModeService.requestModeChange}.
18317
+ *
18318
+ * @param id - Segment id (`stn` / `sfn`) or null.
18319
+ */
18320
+ onShippingModeChange(id) {
18321
+ if (id !== 'stn' && id !== 'sfn') {
18322
+ return;
18323
+ }
18324
+ this.shippingModeActiveId.set(id);
18325
+ if (id === this.shipping.mode()) {
18326
+ return;
18327
+ }
18328
+ this.shipping.requestModeChange(id).subscribe((ok) => {
18329
+ if (!ok) {
18330
+ this.shippingModeActiveId.set(this.shipping.mode());
18331
+ }
18332
+ });
18333
+ }
17902
18334
  visibleRowIds = computed(() => this.rowList().map((row, index) => this.rowId(row, index)), /* @ts-ignore */
17903
18335
  ...(ngDevMode ? [{ debugName: "visibleRowIds" }] : /* istanbul ignore next */ []));
17904
18336
  someRowsExpanded = computed(() => {
@@ -17958,11 +18390,12 @@ class TableComponent {
17958
18390
  return 'border-b border-border dark:border-white/10';
17959
18391
  }
17960
18392
  /**
17961
- * Opaque mode soft fill for header cells (sticky-safe in dark mode).
17962
- * @returns Soft solid background utilities.
18393
+ * Opaque header fill (sticky-safe). Neutral chrome — not mode-accented.
18394
+ * Light `#f0f2f5` / dark elevated ink surface.
18395
+ * @returns Background utility classes.
17963
18396
  */
17964
18397
  headerSoftClass() {
17965
- return this.modeColor.classes().softSolid;
18398
+ return 'bg-[#f0f2f5] dark:bg-[#2a2c31]';
17966
18399
  }
17967
18400
  /**
17968
18401
  * Body row classes — row marker for hover styles, plus hide cell borders on
@@ -18033,7 +18466,7 @@ class TableComponent {
18033
18466
  ? 'border-b-[1.5px] '
18034
18467
  : 'border-b ';
18035
18468
  return (border +
18036
- 'border-border whitespace-normal wrap-break-word px-[1.125rem] py-4 font-medium text-neutral-600 dark:border-white/10 dark:text-neutral-400 ' +
18469
+ '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 ' +
18037
18470
  this.headerSoftClass() +
18038
18471
  ' ' +
18039
18472
  this.stickySurfaceClass(col, 'head') +
@@ -18198,68 +18631,86 @@ class TableComponent {
18198
18631
  this.sortChange.emit({ key, direction });
18199
18632
  }
18200
18633
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: TableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18201
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: TableComponent, isStandalone: true, selector: "africanies-table", inputs: { expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, rowTrackBy: { classPropertyName: "rowTrackBy", publicName: "rowTrackBy", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, stickyHeader: { classPropertyName: "stickyHeader", publicName: "stickyHeader", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, showRefresh: { classPropertyName: "showRefresh", publicName: "showRefresh", isSignal: true, isRequired: false, transformFunction: null }, refreshLabel: { classPropertyName: "refreshLabel", publicName: "refreshLabel", isSignal: true, isRequired: false, transformFunction: null }, refreshing: { classPropertyName: "refreshing", publicName: "refreshing", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingLabel: { classPropertyName: "loadingLabel", publicName: "loadingLabel", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, showFilter: { classPropertyName: "showFilter", publicName: "showFilter", isSignal: true, isRequired: false, transformFunction: null }, filterLabel: { classPropertyName: "filterLabel", publicName: "filterLabel", isSignal: true, isRequired: false, transformFunction: null }, filterCount: { classPropertyName: "filterCount", publicName: "filterCount", isSignal: true, isRequired: false, transformFunction: null }, showExport: { classPropertyName: "showExport", publicName: "showExport", isSignal: true, isRequired: false, transformFunction: null }, exportLabel: { classPropertyName: "exportLabel", publicName: "exportLabel", isSignal: true, isRequired: false, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowExpandChange: "rowExpandChange", sortChange: "sortChange", refreshClick: "refreshClick", filterClick: "filterClick", exportClick: "exportClick", pageChange: "pageChange", sizeChange: "sizeChange" }, host: { classAttribute: "block w-full min-w-0" }, queries: [{ propertyName: "cellDefs", predicate: CellDefDirective, isSignal: true }, { propertyName: "headerCellDefs", predicate: HeaderCellDefDirective, isSignal: true }, { propertyName: "rowDetailDefs", predicate: RowDetailDefDirective, isSignal: true }], ngImport: i0, template: `
18634
+ 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: `
18202
18635
  <div
18203
18636
  class="flex w-full min-w-0 flex-col gap-3"
18204
18637
  [attr.aria-busy]="loading() || refreshing() || null"
18205
18638
  >
18206
- @if (showRefresh() || showFilter() || showExport()) {
18207
- <div class="flex items-center justify-between gap-2">
18208
- <div class="flex items-center gap-2">
18209
- @if (showToolbarRefresh()) {
18210
- <button
18211
- africanies-button
18212
- type="button"
18213
- variant="flat"
18214
- size="sm"
18215
- [disabled]="loading() || refreshing()"
18216
- [attr.aria-label]="refreshLabel()"
18217
- (click)="refreshClick.emit()"
18218
- >
18219
- <africanies-icon
18220
- name="refresh"
18221
- [size]="16"
18222
- [class]="refreshing() ? 'animate-spin' : ''"
18223
- />
18224
- {{ refreshLabel() }}
18225
- </button>
18226
- }
18227
- </div>
18228
- <div class="flex items-center gap-2">
18229
- @if (showFilter()) {
18230
- <button
18231
- africanies-button
18232
- type="button"
18233
- variant="flat"
18234
- size="sm"
18235
- [attr.aria-label]="filterLabel()"
18236
- (click)="filterClick.emit()"
18237
- >
18238
- <africanies-icon name="filter" [size]="16" />
18239
- {{ filterLabel() }}
18240
- @if (filterCount() > 0) {
18241
- <span
18242
- class="inline-flex min-w-5 items-center justify-center rounded-full bg-ink px-1.5 py-0.5 text-caption font-semibold tabular-nums text-white dark:bg-white dark:text-ink"
18639
+ @if (
18640
+ showShippingMode() ||
18641
+ showRefresh() ||
18642
+ showFilter() ||
18643
+ showExport()
18644
+ ) {
18645
+ <div class="flex w-full min-w-0 flex-col gap-3">
18646
+ @if (showShippingMode()) {
18647
+ <africanies-segment
18648
+ class="min-w-0 self-start"
18649
+ [items]="shippingModeItems"
18650
+ [activeId]="shippingModeActiveId()"
18651
+ ariaLabel="Shipping mode"
18652
+ (activeIdChange)="onShippingModeChange($event)"
18653
+ />
18654
+ }
18655
+ @if (showToolbarRefresh() || showFilter() || showExport()) {
18656
+ <div class="flex flex-wrap items-center justify-between gap-2">
18657
+ <div class="flex min-w-0 flex-wrap items-center gap-2">
18658
+ @if (showToolbarRefresh()) {
18659
+ <button
18660
+ africanies-button
18661
+ type="button"
18662
+ variant="flat"
18663
+ size="sm"
18664
+ [disabled]="loading() || refreshing()"
18665
+ [attr.aria-label]="refreshLabel()"
18666
+ (click)="refreshClick.emit()"
18243
18667
  >
18244
- {{ filterCount() }}
18245
- </span>
18668
+ <africanies-icon
18669
+ name="refresh"
18670
+ [size]="16"
18671
+ [class]="refreshing() ? 'animate-spin' : ''"
18672
+ />
18673
+ {{ refreshLabel() }}
18674
+ </button>
18246
18675
  }
18247
- </button>
18248
- }
18249
- @if (showExport()) {
18250
- <button
18251
- africanies-button
18252
- type="button"
18253
- variant="flat"
18254
- size="sm"
18255
- [attr.aria-label]="exportLabel()"
18256
- (click)="exportClick.emit()"
18257
- >
18258
- <africanies-icon name="download" [size]="16" />
18259
- {{ exportLabel() }}
18260
- </button>
18261
- }
18262
- </div>
18676
+ </div>
18677
+ <div class="flex items-center gap-2">
18678
+ @if (showFilter()) {
18679
+ <button
18680
+ africanies-button
18681
+ type="button"
18682
+ variant="flat"
18683
+ size="sm"
18684
+ [attr.aria-label]="filterLabel()"
18685
+ (click)="filterClick.emit()"
18686
+ >
18687
+ <africanies-icon name="filter" [size]="16" />
18688
+ {{ filterLabel() }}
18689
+ @if (filterCount() > 0) {
18690
+ <span
18691
+ class="inline-flex min-w-5 items-center justify-center rounded-full bg-ink px-1.5 py-0.5 text-caption font-semibold tabular-nums text-white dark:bg-white dark:text-ink"
18692
+ >
18693
+ {{ filterCount() }}
18694
+ </span>
18695
+ }
18696
+ </button>
18697
+ }
18698
+ @if (showExport()) {
18699
+ <button
18700
+ africanies-button
18701
+ type="button"
18702
+ variant="flat"
18703
+ size="sm"
18704
+ [attr.aria-label]="exportLabel()"
18705
+ (click)="exportClick.emit()"
18706
+ >
18707
+ <africanies-icon name="download" [size]="16" />
18708
+ {{ exportLabel() }}
18709
+ </button>
18710
+ }
18711
+ </div>
18712
+ </div>
18713
+ }
18263
18714
  </div>
18264
18715
  }
18265
18716
 
@@ -18274,13 +18725,18 @@ class TableComponent {
18274
18725
  }
18275
18726
 
18276
18727
  <div
18277
- class="relative min-w-0 w-full overflow-x-auto rounded-panel border border-border bg-surface dark:border-white/10 dark:bg-ink-surface"
18278
- [class.lg:max-h-[min(70dvh,calc(100dvh-12rem))]]="stickyHeader()"
18279
- [class.lg:overflow-auto]="stickyHeader()"
18728
+ class="relative min-w-0 w-full overflow-hidden rounded-panel border border-border bg-surface dark:border-white/10 dark:bg-ink-surface"
18280
18729
  >
18281
- <table
18282
- class="w-max min-w-full table-auto border-separate border-spacing-0 bg-inherit text-left text-body text-ink dark:text-white"
18730
+ <div
18731
+ #scrollport
18732
+ class="africanies-table-scrollport min-w-0 w-full overflow-x-auto"
18733
+ [class.lg:max-h-[min(70dvh,calc(100dvh-12rem))]]="stickyHeader()"
18734
+ [class.lg:overflow-auto]="stickyHeader() && !paginationLoading()"
18735
+ [class.pointer-events-none]="paginationLoading()"
18283
18736
  >
18737
+ <table
18738
+ class="w-max min-w-full table-auto border-separate border-spacing-0 bg-inherit text-left text-body text-ink dark:text-white"
18739
+ >
18284
18740
  <thead [class]="headerRowClass()">
18285
18741
  <tr>
18286
18742
  @if (isExpandable()) {
@@ -18291,7 +18747,7 @@ class TableComponent {
18291
18747
  @if (someRowsExpanded()) {
18292
18748
  <button
18293
18749
  type="button"
18294
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18750
+ 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"
18295
18751
  aria-label="Collapse all"
18296
18752
  (click)="collapseAllRows()"
18297
18753
  >
@@ -18300,7 +18756,7 @@ class TableComponent {
18300
18756
  } @else {
18301
18757
  <button
18302
18758
  type="button"
18303
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus disabled:cursor-not-allowed disabled:opacity-40 dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
18759
+ 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"
18304
18760
  aria-label="Expand all"
18305
18761
  [disabled]="rowList().length === 0"
18306
18762
  (click)="expandAllRows()"
@@ -18324,7 +18780,7 @@ class TableComponent {
18324
18780
  } @else if (col.sortable) {
18325
18781
  <button
18326
18782
  type="button"
18327
- class="inline-flex cursor-pointer items-center gap-1 rounded-sm font-medium text-neutral-600 hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:text-white"
18783
+ 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"
18328
18784
  (click)="onSortHeaderClick(col.key)"
18329
18785
  >
18330
18786
  <span>{{ col.header }}</span>
@@ -18420,41 +18876,52 @@ class TableComponent {
18420
18876
  [class.border-border]="isRowExpanded(row, i)"
18421
18877
  [class.dark:border-white/10]="isRowExpanded(row, i)"
18422
18878
  >
18423
- <td class="p-0 align-top" [attr.colspan]="columns().length + 1">
18879
+ <td class="p-0 align-top" [attr.colspan]="colSpan()">
18880
+ <!-- Sticky must sit outside overflow-hidden or it pins
18881
+ to the full table width and scrolls with the row. -->
18424
18882
  <div
18425
- class="grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none"
18426
- [style.grid-template-rows]="
18427
- isRowExpanded(row, i) ? '1fr' : '0fr'
18428
- "
18429
- [attr.aria-hidden]="!isRowExpanded(row, i)"
18430
- [class.pointer-events-none]="!isRowExpanded(row, i)"
18883
+ class="africanies-table-expand-panel bg-surface-sunken dark:bg-white/[0.05]"
18884
+ [style.width.px]="scrollportWidthPx() || null"
18885
+ [style.maxWidth.px]="scrollportWidthPx() || null"
18431
18886
  >
18432
18887
  <div
18433
- class="min-h-0 overflow-hidden border-t border-border-strong bg-surface-sunken dark:border-white/10 dark:bg-white/[0.05]"
18888
+ class="grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none"
18889
+ [style.grid-template-rows]="
18890
+ isRowExpanded(row, i) ? '1fr' : '0fr'
18891
+ "
18892
+ [attr.aria-hidden]="!isRowExpanded(row, i)"
18893
+ [class.pointer-events-none]="!isRowExpanded(row, i)"
18434
18894
  >
18435
- <dl
18436
- class="m-0 grid gap-x-6 gap-y-3 px-[1.125rem] py-4 transition-opacity duration-200 ease-out motion-reduce:transition-none sm:grid-cols-2 lg:grid-cols-3"
18437
- [class.opacity-0]="!isRowExpanded(row, i)"
18438
- [class.opacity-100]="isRowExpanded(row, i)"
18895
+ <div
18896
+ class="min-h-0 overflow-x-hidden overflow-y-hidden border-t border-border-strong dark:border-white/10"
18439
18897
  >
18440
- @for (detail of rowDetailDefs(); track detail.label()) {
18441
- <div class="min-w-0">
18442
- <dt
18443
- class="m-0 text-caption font-medium text-neutral-600 dark:text-neutral-400"
18444
- >
18445
- {{ detail.label() }}
18446
- </dt>
18447
- <dd
18448
- class="m-0 mt-0.5 text-body-sm text-ink dark:text-white"
18449
- >
18450
- <ng-container
18451
- [ngTemplateOutlet]="detail.template"
18452
- [ngTemplateOutletContext]="cellContext(row)"
18453
- />
18454
- </dd>
18455
- </div>
18456
- }
18457
- </dl>
18898
+ <dl
18899
+ 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"
18900
+ [class.opacity-0]="!isRowExpanded(row, i)"
18901
+ [class.opacity-100]="isRowExpanded(row, i)"
18902
+ >
18903
+ @for (
18904
+ detail of rowDetailDefs();
18905
+ track detail.label()
18906
+ ) {
18907
+ <div class="min-w-0">
18908
+ <dt
18909
+ class="m-0 text-caption font-medium text-neutral-600 dark:text-neutral-400"
18910
+ >
18911
+ {{ detail.label() }}
18912
+ </dt>
18913
+ <dd
18914
+ class="m-0 mt-0.5 break-words text-body-sm text-ink dark:text-white"
18915
+ >
18916
+ <ng-container
18917
+ [ngTemplateOutlet]="detail.template"
18918
+ [ngTemplateOutletContext]="cellContext(row)"
18919
+ />
18920
+ </dd>
18921
+ </div>
18922
+ }
18923
+ </dl>
18924
+ </div>
18458
18925
  </div>
18459
18926
  </div>
18460
18927
  </td>
@@ -18465,16 +18932,21 @@ class TableComponent {
18465
18932
  }
18466
18933
  </tbody>
18467
18934
  </table>
18935
+ </div>
18468
18936
  @if (paginationLoading()) {
18469
18937
  <div
18470
- class="pointer-events-none absolute inset-0 z-[5] flex items-start justify-center bg-surface/70 px-4 pt-16 dark:bg-ink-950/70"
18938
+ class="absolute inset-0 z-[5] bg-surface/70 dark:bg-ink-950/70"
18471
18939
  data-testid="africanies-table-keep-rows-loading"
18472
18940
  aria-hidden="true"
18473
18941
  >
18474
- <africanies-loading-state
18475
- mode="inline"
18476
- [message]="loadingLabel()"
18477
- />
18942
+ <div
18943
+ class="sticky top-0 flex h-[min(70dvh,100%)] items-center justify-center px-4"
18944
+ >
18945
+ <africanies-loading-state
18946
+ mode="inline"
18947
+ [message]="loadingLabel()"
18948
+ />
18949
+ </div>
18478
18950
  </div>
18479
18951
  }
18480
18952
  </div>
@@ -18489,7 +18961,7 @@ class TableComponent {
18489
18961
  />
18490
18962
  }
18491
18963
  </div>
18492
- `, isInline: true, styles: [":host tr.africanies-table-row:hover>td{background-color:#e9eef5}:host-context(.dark) tr.africanies-table-row:hover>td{background-color:#272729}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ButtonComponent, selector: "button[africanies-button], a[africanies-button]", inputs: ["variant", "size", "disabled", "loading"] }, { kind: "component", type: AfricaniesIconComponent, selector: "africanies-icon", inputs: ["name", "size"] }, { kind: "component", type: EmptyStateComponent, selector: "africanies-empty-state", inputs: ["message", "refreshing"], outputs: ["retry"] }, { kind: "component", type: ErrorIndicatorComponent, selector: "africanies-error-indicator", inputs: ["error", "retryText", "refreshing", "refreshingText", "disabled"], outputs: ["retry"] }, { kind: "component", type: ErrorStateComponent, selector: "africanies-error-state", inputs: ["message", "refreshing"], outputs: ["retry"] }, { kind: "component", type: LoadingStateComponent, selector: "africanies-loading-state", inputs: ["message", "mode"] }, { kind: "component", type: PaginationComponent, selector: "africanies-pagination", inputs: ["meta", "disabled", "loading", "pageSizes"], outputs: ["pageChange", "sizeChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
18964
+ `, 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 });
18493
18965
  }
18494
18966
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: TableComponent, decorators: [{
18495
18967
  type: Component,
@@ -18502,6 +18974,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18502
18974
  ErrorStateComponent,
18503
18975
  LoadingStateComponent,
18504
18976
  PaginationComponent,
18977
+ SegmentComponent,
18505
18978
  ], host: {
18506
18979
  class: 'block w-full min-w-0',
18507
18980
  }, template: `
@@ -18509,63 +18982,81 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18509
18982
  class="flex w-full min-w-0 flex-col gap-3"
18510
18983
  [attr.aria-busy]="loading() || refreshing() || null"
18511
18984
  >
18512
- @if (showRefresh() || showFilter() || showExport()) {
18513
- <div class="flex items-center justify-between gap-2">
18514
- <div class="flex items-center gap-2">
18515
- @if (showToolbarRefresh()) {
18516
- <button
18517
- africanies-button
18518
- type="button"
18519
- variant="flat"
18520
- size="sm"
18521
- [disabled]="loading() || refreshing()"
18522
- [attr.aria-label]="refreshLabel()"
18523
- (click)="refreshClick.emit()"
18524
- >
18525
- <africanies-icon
18526
- name="refresh"
18527
- [size]="16"
18528
- [class]="refreshing() ? 'animate-spin' : ''"
18529
- />
18530
- {{ refreshLabel() }}
18531
- </button>
18532
- }
18533
- </div>
18534
- <div class="flex items-center gap-2">
18535
- @if (showFilter()) {
18536
- <button
18537
- africanies-button
18538
- type="button"
18539
- variant="flat"
18540
- size="sm"
18541
- [attr.aria-label]="filterLabel()"
18542
- (click)="filterClick.emit()"
18543
- >
18544
- <africanies-icon name="filter" [size]="16" />
18545
- {{ filterLabel() }}
18546
- @if (filterCount() > 0) {
18547
- <span
18548
- class="inline-flex min-w-5 items-center justify-center rounded-full bg-ink px-1.5 py-0.5 text-caption font-semibold tabular-nums text-white dark:bg-white dark:text-ink"
18985
+ @if (
18986
+ showShippingMode() ||
18987
+ showRefresh() ||
18988
+ showFilter() ||
18989
+ showExport()
18990
+ ) {
18991
+ <div class="flex w-full min-w-0 flex-col gap-3">
18992
+ @if (showShippingMode()) {
18993
+ <africanies-segment
18994
+ class="min-w-0 self-start"
18995
+ [items]="shippingModeItems"
18996
+ [activeId]="shippingModeActiveId()"
18997
+ ariaLabel="Shipping mode"
18998
+ (activeIdChange)="onShippingModeChange($event)"
18999
+ />
19000
+ }
19001
+ @if (showToolbarRefresh() || showFilter() || showExport()) {
19002
+ <div class="flex flex-wrap items-center justify-between gap-2">
19003
+ <div class="flex min-w-0 flex-wrap items-center gap-2">
19004
+ @if (showToolbarRefresh()) {
19005
+ <button
19006
+ africanies-button
19007
+ type="button"
19008
+ variant="flat"
19009
+ size="sm"
19010
+ [disabled]="loading() || refreshing()"
19011
+ [attr.aria-label]="refreshLabel()"
19012
+ (click)="refreshClick.emit()"
18549
19013
  >
18550
- {{ filterCount() }}
18551
- </span>
19014
+ <africanies-icon
19015
+ name="refresh"
19016
+ [size]="16"
19017
+ [class]="refreshing() ? 'animate-spin' : ''"
19018
+ />
19019
+ {{ refreshLabel() }}
19020
+ </button>
18552
19021
  }
18553
- </button>
18554
- }
18555
- @if (showExport()) {
18556
- <button
18557
- africanies-button
18558
- type="button"
18559
- variant="flat"
18560
- size="sm"
18561
- [attr.aria-label]="exportLabel()"
18562
- (click)="exportClick.emit()"
18563
- >
18564
- <africanies-icon name="download" [size]="16" />
18565
- {{ exportLabel() }}
18566
- </button>
18567
- }
18568
- </div>
19022
+ </div>
19023
+ <div class="flex items-center gap-2">
19024
+ @if (showFilter()) {
19025
+ <button
19026
+ africanies-button
19027
+ type="button"
19028
+ variant="flat"
19029
+ size="sm"
19030
+ [attr.aria-label]="filterLabel()"
19031
+ (click)="filterClick.emit()"
19032
+ >
19033
+ <africanies-icon name="filter" [size]="16" />
19034
+ {{ filterLabel() }}
19035
+ @if (filterCount() > 0) {
19036
+ <span
19037
+ class="inline-flex min-w-5 items-center justify-center rounded-full bg-ink px-1.5 py-0.5 text-caption font-semibold tabular-nums text-white dark:bg-white dark:text-ink"
19038
+ >
19039
+ {{ filterCount() }}
19040
+ </span>
19041
+ }
19042
+ </button>
19043
+ }
19044
+ @if (showExport()) {
19045
+ <button
19046
+ africanies-button
19047
+ type="button"
19048
+ variant="flat"
19049
+ size="sm"
19050
+ [attr.aria-label]="exportLabel()"
19051
+ (click)="exportClick.emit()"
19052
+ >
19053
+ <africanies-icon name="download" [size]="16" />
19054
+ {{ exportLabel() }}
19055
+ </button>
19056
+ }
19057
+ </div>
19058
+ </div>
19059
+ }
18569
19060
  </div>
18570
19061
  }
18571
19062
 
@@ -18580,13 +19071,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18580
19071
  }
18581
19072
 
18582
19073
  <div
18583
- class="relative min-w-0 w-full overflow-x-auto rounded-panel border border-border bg-surface dark:border-white/10 dark:bg-ink-surface"
18584
- [class.lg:max-h-[min(70dvh,calc(100dvh-12rem))]]="stickyHeader()"
18585
- [class.lg:overflow-auto]="stickyHeader()"
19074
+ class="relative min-w-0 w-full overflow-hidden rounded-panel border border-border bg-surface dark:border-white/10 dark:bg-ink-surface"
18586
19075
  >
18587
- <table
18588
- class="w-max min-w-full table-auto border-separate border-spacing-0 bg-inherit text-left text-body text-ink dark:text-white"
19076
+ <div
19077
+ #scrollport
19078
+ class="africanies-table-scrollport min-w-0 w-full overflow-x-auto"
19079
+ [class.lg:max-h-[min(70dvh,calc(100dvh-12rem))]]="stickyHeader()"
19080
+ [class.lg:overflow-auto]="stickyHeader() && !paginationLoading()"
19081
+ [class.pointer-events-none]="paginationLoading()"
18589
19082
  >
19083
+ <table
19084
+ class="w-max min-w-full table-auto border-separate border-spacing-0 bg-inherit text-left text-body text-ink dark:text-white"
19085
+ >
18590
19086
  <thead [class]="headerRowClass()">
18591
19087
  <tr>
18592
19088
  @if (isExpandable()) {
@@ -18597,7 +19093,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18597
19093
  @if (someRowsExpanded()) {
18598
19094
  <button
18599
19095
  type="button"
18600
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
19096
+ 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"
18601
19097
  aria-label="Collapse all"
18602
19098
  (click)="collapseAllRows()"
18603
19099
  >
@@ -18606,7 +19102,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18606
19102
  } @else {
18607
19103
  <button
18608
19104
  type="button"
18609
- class="inline-flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-surface hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus disabled:cursor-not-allowed disabled:opacity-40 dark:text-neutral-400 dark:hover:bg-white/10 dark:hover:text-white"
19105
+ 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"
18610
19106
  aria-label="Expand all"
18611
19107
  [disabled]="rowList().length === 0"
18612
19108
  (click)="expandAllRows()"
@@ -18630,7 +19126,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18630
19126
  } @else if (col.sortable) {
18631
19127
  <button
18632
19128
  type="button"
18633
- class="inline-flex cursor-pointer items-center gap-1 rounded-sm font-medium text-neutral-600 hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-focus dark:text-neutral-400 dark:hover:text-white"
19129
+ 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"
18634
19130
  (click)="onSortHeaderClick(col.key)"
18635
19131
  >
18636
19132
  <span>{{ col.header }}</span>
@@ -18726,41 +19222,52 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18726
19222
  [class.border-border]="isRowExpanded(row, i)"
18727
19223
  [class.dark:border-white/10]="isRowExpanded(row, i)"
18728
19224
  >
18729
- <td class="p-0 align-top" [attr.colspan]="columns().length + 1">
19225
+ <td class="p-0 align-top" [attr.colspan]="colSpan()">
19226
+ <!-- Sticky must sit outside overflow-hidden or it pins
19227
+ to the full table width and scrolls with the row. -->
18730
19228
  <div
18731
- class="grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none"
18732
- [style.grid-template-rows]="
18733
- isRowExpanded(row, i) ? '1fr' : '0fr'
18734
- "
18735
- [attr.aria-hidden]="!isRowExpanded(row, i)"
18736
- [class.pointer-events-none]="!isRowExpanded(row, i)"
19229
+ class="africanies-table-expand-panel bg-surface-sunken dark:bg-white/[0.05]"
19230
+ [style.width.px]="scrollportWidthPx() || null"
19231
+ [style.maxWidth.px]="scrollportWidthPx() || null"
18737
19232
  >
18738
19233
  <div
18739
- class="min-h-0 overflow-hidden border-t border-border-strong bg-surface-sunken dark:border-white/10 dark:bg-white/[0.05]"
19234
+ class="grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none"
19235
+ [style.grid-template-rows]="
19236
+ isRowExpanded(row, i) ? '1fr' : '0fr'
19237
+ "
19238
+ [attr.aria-hidden]="!isRowExpanded(row, i)"
19239
+ [class.pointer-events-none]="!isRowExpanded(row, i)"
18740
19240
  >
18741
- <dl
18742
- class="m-0 grid gap-x-6 gap-y-3 px-[1.125rem] py-4 transition-opacity duration-200 ease-out motion-reduce:transition-none sm:grid-cols-2 lg:grid-cols-3"
18743
- [class.opacity-0]="!isRowExpanded(row, i)"
18744
- [class.opacity-100]="isRowExpanded(row, i)"
19241
+ <div
19242
+ class="min-h-0 overflow-x-hidden overflow-y-hidden border-t border-border-strong dark:border-white/10"
18745
19243
  >
18746
- @for (detail of rowDetailDefs(); track detail.label()) {
18747
- <div class="min-w-0">
18748
- <dt
18749
- class="m-0 text-caption font-medium text-neutral-600 dark:text-neutral-400"
18750
- >
18751
- {{ detail.label() }}
18752
- </dt>
18753
- <dd
18754
- class="m-0 mt-0.5 text-body-sm text-ink dark:text-white"
18755
- >
18756
- <ng-container
18757
- [ngTemplateOutlet]="detail.template"
18758
- [ngTemplateOutletContext]="cellContext(row)"
18759
- />
18760
- </dd>
18761
- </div>
18762
- }
18763
- </dl>
19244
+ <dl
19245
+ 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"
19246
+ [class.opacity-0]="!isRowExpanded(row, i)"
19247
+ [class.opacity-100]="isRowExpanded(row, i)"
19248
+ >
19249
+ @for (
19250
+ detail of rowDetailDefs();
19251
+ track detail.label()
19252
+ ) {
19253
+ <div class="min-w-0">
19254
+ <dt
19255
+ class="m-0 text-caption font-medium text-neutral-600 dark:text-neutral-400"
19256
+ >
19257
+ {{ detail.label() }}
19258
+ </dt>
19259
+ <dd
19260
+ class="m-0 mt-0.5 break-words text-body-sm text-ink dark:text-white"
19261
+ >
19262
+ <ng-container
19263
+ [ngTemplateOutlet]="detail.template"
19264
+ [ngTemplateOutletContext]="cellContext(row)"
19265
+ />
19266
+ </dd>
19267
+ </div>
19268
+ }
19269
+ </dl>
19270
+ </div>
18764
19271
  </div>
18765
19272
  </div>
18766
19273
  </td>
@@ -18771,16 +19278,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18771
19278
  }
18772
19279
  </tbody>
18773
19280
  </table>
19281
+ </div>
18774
19282
  @if (paginationLoading()) {
18775
19283
  <div
18776
- class="pointer-events-none absolute inset-0 z-[5] flex items-start justify-center bg-surface/70 px-4 pt-16 dark:bg-ink-950/70"
19284
+ class="absolute inset-0 z-[5] bg-surface/70 dark:bg-ink-950/70"
18777
19285
  data-testid="africanies-table-keep-rows-loading"
18778
19286
  aria-hidden="true"
18779
19287
  >
18780
- <africanies-loading-state
18781
- mode="inline"
18782
- [message]="loadingLabel()"
18783
- />
19288
+ <div
19289
+ class="sticky top-0 flex h-[min(70dvh,100%)] items-center justify-center px-4"
19290
+ >
19291
+ <africanies-loading-state
19292
+ mode="inline"
19293
+ [message]="loadingLabel()"
19294
+ />
19295
+ </div>
18784
19296
  </div>
18785
19297
  }
18786
19298
  </div>
@@ -18795,8 +19307,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
18795
19307
  />
18796
19308
  }
18797
19309
  </div>
18798
- `, styles: [":host tr.africanies-table-row:hover>td{background-color:#e9eef5}:host-context(.dark) tr.africanies-table-row:hover>td{background-color:#272729}\n"] }]
18799
- }], propDecorators: { expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], rowTrackBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowTrackBy", required: false }] }], rowExpandChange: [{ type: i0.Output, args: ["rowExpandChange"] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], stickyHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "stickyHeader", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }], showRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRefresh", required: false }] }], refreshLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "refreshLabel", required: false }] }], refreshing: [{ type: i0.Input, args: [{ isSignal: true, alias: "refreshing", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], loadingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingLabel", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], showFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFilter", required: false }] }], filterLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterLabel", required: false }] }], filterCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterCount", required: false }] }], showExport: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExport", required: false }] }], exportLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportLabel", required: false }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], refreshClick: [{ type: i0.Output, args: ["refreshClick"] }], filterClick: [{ type: i0.Output, args: ["filterClick"] }], exportClick: [{ type: i0.Output, args: ["exportClick"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], sizeChange: [{ type: i0.Output, args: ["sizeChange"] }], cellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => CellDefDirective), { isSignal: true }] }], headerCellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => HeaderCellDefDirective), { isSignal: true }] }], rowDetailDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => RowDetailDefDirective), { isSignal: true }] }] } });
19310
+ `, 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"] }]
19311
+ }], 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 }] }] } });
18800
19312
 
18801
19313
  /**
18802
19314
  * Presentational table with template-based cells.
@@ -19380,6 +19892,7 @@ class AfricaniesNavigationModule {
19380
19892
  BreadcrumbComponent,
19381
19893
  TabsComponent,
19382
19894
  SegmentComponent,
19895
+ ShippingModeSwitchComponent,
19383
19896
  SideNavComponent] });
19384
19897
  }
19385
19898
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AfricaniesNavigationModule, decorators: [{