@africanies/angular-web-sdk 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) 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/3735952477975556635/dist/libs/africanies-ui/README.md +68 -0
  8. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
  9. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
  10. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +5 -0
  11. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/assets/carriers/dhl.svg +7 -0
  12. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +19998 -0
  13. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -0
  14. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/package.json +35 -0
  15. package/.nx/cache/3735952477975556635/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +5616 -0
  16. package/.nx/cache/run.json +17 -17
  17. package/.nx/cache/terminalOutputs/1149535958171016935 +21 -0
  18. package/.nx/cache/terminalOutputs/3735952477975556635 +21 -0
  19. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db +0 -0
  20. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-shm +0 -0
  21. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-wal +0 -0
  22. package/.nx/workspace-data/d/daemon.log +1430 -0
  23. package/.nx/workspace-data/d/server-process.json +2 -2
  24. package/.nx/workspace-data/file-map.json +3332 -3334
  25. package/.nx/workspace-data/nx_files.nxt +0 -0
  26. package/.nx/workspace-data/project-graph.json +2 -2
  27. package/apps/playground/src/app/pages/chip-page.ts +11 -1
  28. package/apps/playground/src/app/pages/tokens-page.ts +2 -2
  29. package/apps/playground/src/app/snippets/chip.snippets.ts +5 -0
  30. package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +8 -8
  31. package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -1
  32. package/dist/libs/africanies-theme/tailwind-preset.cjs +3 -3
  33. package/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +5 -3
  34. package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +869 -371
  35. package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -1
  36. package/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +135 -40
  37. package/libs/africanies-theme/src/lib/mode-color.safelist.ts +2 -2
  38. package/libs/africanies-theme/src/lib/mode-color.service.spec.ts +9 -7
  39. package/libs/africanies-theme/src/lib/mode-color.service.ts +9 -7
  40. package/libs/africanies-theme/tailwind-preset.cjs +3 -3
  41. package/libs/africanies-ui/src/lib/avatar/avatar-menu.component.ts +25 -21
  42. package/libs/africanies-ui/src/lib/button/button.component.spec.ts +13 -8
  43. package/libs/africanies-ui/src/lib/chip/chip.component.ts +74 -6
  44. package/libs/africanies-ui/src/lib/layout/app-shell-header.component.ts +35 -12
  45. package/libs/africanies-ui/src/lib/layout/header-greeting.util.ts +2 -0
  46. package/libs/africanies-ui/src/lib/layout/header-weather.spec.ts +120 -9
  47. package/libs/africanies-ui/src/lib/layout/header-weather.ts +79 -18
  48. package/libs/africanies-ui/src/lib/navigation/segment/segment.component.ts +98 -22
  49. package/libs/africanies-ui/src/lib/navigation/shipping-mode-switch/shipping-mode-switch.component.ts +193 -65
  50. package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.html +4 -2
  51. package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.ts +3 -1
  52. package/libs/africanies-ui/src/lib/table/table.component.spec.ts +3 -0
  53. package/libs/africanies-ui/src/lib/table/table.component.ts +197 -57
  54. package/package.json +1 -1
  55. package/tools/build-icon-sprite.mjs +1 -1
@@ -58,15 +58,17 @@
58
58
  </button>
59
59
  </div>
60
60
 
61
+ <ng-content select="[pre-nav]" />
62
+
61
63
  <nav
62
64
  class="africanies-overlay-scroll relative min-h-0 flex-1 overflow-y-auto overflow-x-visible px-3 py-3"
63
65
  >
64
66
  @if (!collapsed() && expandParents() && hasOpenBranches()) {
65
67
  <!-- Zero-height sticky host: keeps the control pinned without reserving a row. -->
66
- <div class="sticky top-0 z-10 h-0 overflow-visible">
68
+ <div class="sticky top-0 z-20 h-0 overflow-visible">
67
69
  <button
68
70
  type="button"
69
- 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"
71
+ 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"
70
72
  data-onboarding="nav-branches-collapse"
71
73
  aria-label="Collapse all"
72
74
  (click)="collapseAllBranches()"
@@ -47,7 +47,9 @@ const DEFAULT_LINK_ACTIVE: IsActiveMatchOptions = {
47
47
  * **Expanded rail:** branches start open by default (`expandBranchesByDefault`).
48
48
  * Clicking a parent only opens/closes the branch (no route change). When any
49
49
  * branch is open, a sticky collapse-all control sits above the list (top right).
50
- * The rail expand/collapse control stays pinned in the logo header.
50
+ * The rail expand/collapse control stays pinned in the logo header. Project
51
+ * chrome above the scrollable list with `pre-nav` (e.g. shipping modes) or
52
+ * below with `footer`.
51
53
  * **Collapsed rail:** clicking a parent expands the rail, opens the
52
54
  * branch, and activates the first enabled child (router or `activeId`).
53
55
  *
@@ -1,6 +1,8 @@
1
1
  import { Component, signal } from '@angular/core';
2
2
  import { ComponentFixture, TestBed } from '@angular/core/testing';
3
3
 
4
+ import { of } from 'rxjs';
5
+
4
6
  import { ShippingModeService } from '@africanies/africanies-core';
5
7
  import type { ShippingMode } from '@africanies/africanies-models';
6
8
 
@@ -45,6 +47,7 @@ describe('TableComponent keep-rows loading', () => {
45
47
  provide: ShippingModeService,
46
48
  useValue: {
47
49
  mode: mode.asReadonly(),
50
+ requestModeChange: () => of(true),
48
51
  },
49
52
  },
50
53
  ],
@@ -1,27 +1,37 @@
1
1
  import { NgTemplateOutlet } from '@angular/common';
2
2
  import {
3
+ afterNextRender,
3
4
  booleanAttribute,
4
5
  ChangeDetectionStrategy,
5
6
  Component,
6
7
  computed,
7
8
  contentChildren,
9
+ DestroyRef,
10
+ effect,
11
+ ElementRef,
8
12
  inject,
9
13
  input,
10
14
  numberAttribute,
11
15
  output,
12
16
  signal,
13
17
  TemplateRef,
18
+ viewChild,
14
19
  } from '@angular/core';
15
20
 
21
+ import { ShippingModeService } from '@africanies/africanies-core';
16
22
  import { AfricaniesIconComponent } from '@africanies/africanies-icons';
17
- import type { PaginationMetaModel } from '@africanies/africanies-models';
18
- import { ModeColorService } from '@africanies/africanies-theme';
23
+ import type {
24
+ PaginationMetaModel,
25
+ ShippingMode,
26
+ } from '@africanies/africanies-models';
19
27
 
20
28
  import { ButtonComponent } from '../button/button.component';
21
29
  import { EmptyStateComponent } from '../feedback/empty-state.component';
22
30
  import { ErrorIndicatorComponent } from '../feedback/error-indicator.component';
23
31
  import { ErrorStateComponent } from '../feedback/error-state.component';
24
32
  import { LoadingStateComponent } from '../feedback/loading-state.component';
33
+ import type { AfricaniesNavItem } from '../navigation/nav-item';
34
+ import { SegmentComponent } from '../navigation/segment/segment.component';
25
35
  import { PaginationComponent } from '../pagination/pagination.component';
26
36
  import { CellDefDirective } from './cell-def.directive';
27
37
  import { HeaderCellDefDirective } from './header-cell-def.directive';
@@ -35,8 +45,10 @@ import { TableColumn, TableSortChange } from './table-column';
35
45
  * `<ng-template africaniesCellDef="key">` templates. Columns without a matching
36
46
  * template fall back to rendering `row[key]` as plain text.
37
47
  *
38
- * Optional toolbar: top-left **Refresh** (`showRefresh` {@link refreshClick}).
39
- * Hidden while the body shows empty or error — those states expose Retry instead.
48
+ * Optional toolbar: top-left **shipping mode** segment (on by default;
49
+ * `[showShippingMode]="false"` to hide) and **Refresh** (`showRefresh`
50
+ * {@link refreshClick}). Refresh is hidden while
51
+ * the body shows empty or error — those states expose Retry instead.
40
52
  * While {@link refreshing} is true the rows stay on screen — the refresh icon
41
53
  * spins (do not swap to a blocking loader). Use {@link loading} when the page
42
54
  * of data is changing (pagination / size): rows stay on screen, a keep-rows
@@ -65,8 +77,9 @@ import { TableColumn, TableSortChange } from './table-column';
65
77
  * and never reorder local rows.
66
78
  *
67
79
  * **Expandable rows:** project `<ng-template africaniesRowDetail="Label" let-row>`
68
- * templates. A leading chevron column appears; the expanded panel renders each
69
- * label with its template value in a responsive grid.
80
+ * templates. A leading chevron column appears; the expanded panel sticks to the
81
+ * scrollport width (so it does not scroll horizontally with the columns) and
82
+ * lays out label/value pairs in a wrapping auto-fill grid.
70
83
  *
71
84
  * @typeParam T - Row record shape.
72
85
  *
@@ -121,6 +134,7 @@ import { TableColumn, TableSortChange } from './table-column';
121
134
  ErrorStateComponent,
122
135
  LoadingStateComponent,
123
136
  PaginationComponent,
137
+ SegmentComponent,
124
138
  ],
125
139
  host: {
126
140
  class: 'block w-full min-w-0',
@@ -134,15 +148,46 @@ import { TableColumn, TableSortChange } from './table-column';
134
148
  :host-context(.dark) tr.africanies-table-row:hover > td {
135
149
  background-color: #272729;
136
150
  }
151
+
152
+ /* Expand panel sticks to the scrollport (not the wide table). Width is
153
+ also set from measured clientWidth so it stays viewport-bound even
154
+ when container query units are flaky inside tables. */
155
+ :host .africanies-table-scrollport {
156
+ container-type: inline-size;
157
+ container-name: africanies-table;
158
+ }
159
+ :host .africanies-table-expand-panel {
160
+ position: sticky;
161
+ left: 0;
162
+ z-index: 1;
163
+ box-sizing: border-box;
164
+ width: 100cqi;
165
+ max-width: 100cqi;
166
+ overflow-x: hidden;
167
+ }
137
168
  `,
138
169
  template: `
139
170
  <div
140
171
  class="flex w-full min-w-0 flex-col gap-3"
141
172
  [attr.aria-busy]="loading() || refreshing() || null"
142
173
  >
143
- @if (showRefresh() || showFilter() || showExport()) {
144
- <div class="flex items-center justify-between gap-2">
145
- <div class="flex items-center gap-2">
174
+ @if (
175
+ showShippingMode() ||
176
+ showRefresh() ||
177
+ showFilter() ||
178
+ showExport()
179
+ ) {
180
+ <div class="flex flex-wrap items-center justify-between gap-2">
181
+ <div class="flex min-w-0 flex-wrap items-center gap-2">
182
+ @if (showShippingMode()) {
183
+ <africanies-segment
184
+ class="min-w-0"
185
+ [items]="shippingModeItems"
186
+ [activeId]="shippingModeActiveId()"
187
+ ariaLabel="Shipping mode"
188
+ (activeIdChange)="onShippingModeChange($event)"
189
+ />
190
+ }
146
191
  @if (showToolbarRefresh()) {
147
192
  <button
148
193
  africanies-button
@@ -211,13 +256,18 @@ import { TableColumn, TableSortChange } from './table-column';
211
256
  }
212
257
 
213
258
  <div
214
- 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"
215
- [class.lg:max-h-[min(70dvh,calc(100dvh-12rem))]]="stickyHeader()"
216
- [class.lg:overflow-auto]="stickyHeader()"
259
+ class="relative min-w-0 w-full overflow-hidden rounded-panel border border-border bg-surface dark:border-white/10 dark:bg-ink-surface"
217
260
  >
218
- <table
219
- class="w-max min-w-full table-auto border-separate border-spacing-0 bg-inherit text-left text-body text-ink dark:text-white"
261
+ <div
262
+ #scrollport
263
+ class="africanies-table-scrollport min-w-0 w-full overflow-x-auto"
264
+ [class.lg:max-h-[min(70dvh,calc(100dvh-12rem))]]="stickyHeader()"
265
+ [class.lg:overflow-auto]="stickyHeader() && !paginationLoading()"
266
+ [class.pointer-events-none]="paginationLoading()"
220
267
  >
268
+ <table
269
+ class="w-max min-w-full table-auto border-separate border-spacing-0 bg-inherit text-left text-body text-ink dark:text-white"
270
+ >
221
271
  <thead [class]="headerRowClass()">
222
272
  <tr>
223
273
  @if (isExpandable()) {
@@ -228,7 +278,7 @@ import { TableColumn, TableSortChange } from './table-column';
228
278
  @if (someRowsExpanded()) {
229
279
  <button
230
280
  type="button"
231
- 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"
281
+ 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"
232
282
  aria-label="Collapse all"
233
283
  (click)="collapseAllRows()"
234
284
  >
@@ -237,7 +287,7 @@ import { TableColumn, TableSortChange } from './table-column';
237
287
  } @else {
238
288
  <button
239
289
  type="button"
240
- 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"
290
+ 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"
241
291
  aria-label="Expand all"
242
292
  [disabled]="rowList().length === 0"
243
293
  (click)="expandAllRows()"
@@ -261,7 +311,7 @@ import { TableColumn, TableSortChange } from './table-column';
261
311
  } @else if (col.sortable) {
262
312
  <button
263
313
  type="button"
264
- 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"
314
+ 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"
265
315
  (click)="onSortHeaderClick(col.key)"
266
316
  >
267
317
  <span>{{ col.header }}</span>
@@ -357,41 +407,52 @@ import { TableColumn, TableSortChange } from './table-column';
357
407
  [class.border-border]="isRowExpanded(row, i)"
358
408
  [class.dark:border-white/10]="isRowExpanded(row, i)"
359
409
  >
360
- <td class="p-0 align-top" [attr.colspan]="columns().length + 1">
410
+ <td class="p-0 align-top" [attr.colspan]="colSpan()">
411
+ <!-- Sticky must sit outside overflow-hidden or it pins
412
+ to the full table width and scrolls with the row. -->
361
413
  <div
362
- class="grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none"
363
- [style.grid-template-rows]="
364
- isRowExpanded(row, i) ? '1fr' : '0fr'
365
- "
366
- [attr.aria-hidden]="!isRowExpanded(row, i)"
367
- [class.pointer-events-none]="!isRowExpanded(row, i)"
414
+ class="africanies-table-expand-panel bg-surface-sunken dark:bg-white/[0.05]"
415
+ [style.width.px]="scrollportWidthPx() || null"
416
+ [style.maxWidth.px]="scrollportWidthPx() || null"
368
417
  >
369
418
  <div
370
- class="min-h-0 overflow-hidden border-t border-border-strong bg-surface-sunken dark:border-white/10 dark:bg-white/[0.05]"
419
+ class="grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none"
420
+ [style.grid-template-rows]="
421
+ isRowExpanded(row, i) ? '1fr' : '0fr'
422
+ "
423
+ [attr.aria-hidden]="!isRowExpanded(row, i)"
424
+ [class.pointer-events-none]="!isRowExpanded(row, i)"
371
425
  >
372
- <dl
373
- 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"
374
- [class.opacity-0]="!isRowExpanded(row, i)"
375
- [class.opacity-100]="isRowExpanded(row, i)"
426
+ <div
427
+ class="min-h-0 overflow-x-hidden overflow-y-hidden border-t border-border-strong dark:border-white/10"
376
428
  >
377
- @for (detail of rowDetailDefs(); track detail.label()) {
378
- <div class="min-w-0">
379
- <dt
380
- class="m-0 text-caption font-medium text-neutral-600 dark:text-neutral-400"
381
- >
382
- {{ detail.label() }}
383
- </dt>
384
- <dd
385
- class="m-0 mt-0.5 text-body-sm text-ink dark:text-white"
386
- >
387
- <ng-container
388
- [ngTemplateOutlet]="detail.template"
389
- [ngTemplateOutletContext]="cellContext(row)"
390
- />
391
- </dd>
392
- </div>
393
- }
394
- </dl>
429
+ <dl
430
+ 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"
431
+ [class.opacity-0]="!isRowExpanded(row, i)"
432
+ [class.opacity-100]="isRowExpanded(row, i)"
433
+ >
434
+ @for (
435
+ detail of rowDetailDefs();
436
+ track detail.label()
437
+ ) {
438
+ <div class="min-w-0">
439
+ <dt
440
+ class="m-0 text-caption font-medium text-neutral-600 dark:text-neutral-400"
441
+ >
442
+ {{ detail.label() }}
443
+ </dt>
444
+ <dd
445
+ class="m-0 mt-0.5 break-words text-body-sm text-ink dark:text-white"
446
+ >
447
+ <ng-container
448
+ [ngTemplateOutlet]="detail.template"
449
+ [ngTemplateOutletContext]="cellContext(row)"
450
+ />
451
+ </dd>
452
+ </div>
453
+ }
454
+ </dl>
455
+ </div>
395
456
  </div>
396
457
  </div>
397
458
  </td>
@@ -402,16 +463,21 @@ import { TableColumn, TableSortChange } from './table-column';
402
463
  }
403
464
  </tbody>
404
465
  </table>
466
+ </div>
405
467
  @if (paginationLoading()) {
406
468
  <div
407
- 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"
469
+ class="absolute inset-0 z-[5] bg-surface/70 dark:bg-ink-950/70"
408
470
  data-testid="africanies-table-keep-rows-loading"
409
471
  aria-hidden="true"
410
472
  >
411
- <africanies-loading-state
412
- mode="inline"
413
- [message]="loadingLabel()"
414
- />
473
+ <div
474
+ class="sticky top-0 flex h-[min(70dvh,100%)] items-center justify-center px-4"
475
+ >
476
+ <africanies-loading-state
477
+ mode="inline"
478
+ [message]="loadingLabel()"
479
+ />
480
+ </div>
415
481
  </div>
416
482
  }
417
483
  </div>
@@ -429,7 +495,51 @@ import { TableColumn, TableSortChange } from './table-column';
429
495
  `,
430
496
  })
431
497
  export class TableComponent<T = unknown> {
432
- protected readonly modeColor = inject(ModeColorService);
498
+ private readonly destroyRef = inject(DestroyRef);
499
+ protected readonly shipping = inject(ShippingModeService);
500
+
501
+ /** Local STN / SFN segment options for {@link showShippingMode}. */
502
+ protected readonly shippingModeItems: AfricaniesNavItem[] = [
503
+ { id: 'stn', label: 'Shipping to Nigeria' },
504
+ { id: 'sfn', label: 'Shipping from Nigeria' },
505
+ ];
506
+
507
+ /**
508
+ * Segment selection mirror of {@link ShippingModeService.mode}. Kept local
509
+ * so a denied {@link ShippingModeService.requestModeChange} can snap the
510
+ * pills back when the service mode did not change.
511
+ */
512
+ protected readonly shippingModeActiveId = signal<string>('sfn');
513
+
514
+ /** Scrollport that owns horizontal overflow for the main grid. */
515
+ private readonly scrollport =
516
+ viewChild<ElementRef<HTMLElement>>('scrollport');
517
+
518
+ /**
519
+ * Visible width of {@link scrollport}. Applied to the expand panel so
520
+ * details stay within the viewport and do not scroll with the columns.
521
+ */
522
+ protected readonly scrollportWidthPx = signal(0);
523
+
524
+ constructor() {
525
+ effect(() => {
526
+ this.shippingModeActiveId.set(this.shipping.mode());
527
+ });
528
+
529
+ afterNextRender(() => {
530
+ const el = this.scrollport()?.nativeElement;
531
+ if (!el) {
532
+ return;
533
+ }
534
+ const sync = (): void => {
535
+ this.scrollportWidthPx.set(el.clientWidth);
536
+ };
537
+ sync();
538
+ const observer = new ResizeObserver(sync);
539
+ observer.observe(el);
540
+ this.destroyRef.onDestroy(() => observer.disconnect());
541
+ });
542
+ }
433
543
 
434
544
  /**
435
545
  * When false, hides row expansion even if {@link RowDetailDefDirective}
@@ -470,6 +580,14 @@ export class TableComponent<T = unknown> {
470
580
  */
471
581
  readonly sort = input<TableSortChange | null>(null);
472
582
 
583
+ /**
584
+ * Show the Shipping to / from Nigeria segment above the table (top-left).
585
+ * On by default; set `[showShippingMode]="false"` to hide (e.g. modals).
586
+ * Writes through {@link ShippingModeService.requestModeChange} so feature
587
+ * guards still run.
588
+ */
589
+ readonly showShippingMode = input(true, { transform: booleanAttribute });
590
+
473
591
  /**
474
592
  * Show a Refresh button above the table (top-left) once rows are on screen.
475
593
  * Hidden during in-grid empty/error — those use Retry in the body. Host refetches
@@ -626,6 +744,27 @@ export class TableComponent<T = unknown> {
626
744
  () => this.showRefresh() && this.bodyKind() === 'rows',
627
745
  );
628
746
 
747
+ /**
748
+ * Applies a shipping-mode segment selection through
749
+ * {@link ShippingModeService.requestModeChange}.
750
+ *
751
+ * @param id - Segment id (`stn` / `sfn`) or null.
752
+ */
753
+ protected onShippingModeChange(id: string | null): void {
754
+ if (id !== 'stn' && id !== 'sfn') {
755
+ return;
756
+ }
757
+ this.shippingModeActiveId.set(id);
758
+ if (id === this.shipping.mode()) {
759
+ return;
760
+ }
761
+ this.shipping.requestModeChange(id as ShippingMode).subscribe((ok) => {
762
+ if (!ok) {
763
+ this.shippingModeActiveId.set(this.shipping.mode());
764
+ }
765
+ });
766
+ }
767
+
629
768
  protected readonly visibleRowIds = computed(() =>
630
769
  this.rowList().map((row, index) => this.rowId(row, index)),
631
770
  );
@@ -691,11 +830,12 @@ export class TableComponent<T = unknown> {
691
830
  }
692
831
 
693
832
  /**
694
- * Opaque mode soft fill for header cells (sticky-safe in dark mode).
695
- * @returns Soft solid background utilities.
833
+ * Opaque header fill (sticky-safe). Neutral chrome — not mode-accented.
834
+ * Light `#f0f2f5` / dark elevated ink surface.
835
+ * @returns Background utility classes.
696
836
  */
697
837
  protected headerSoftClass(): string {
698
- return this.modeColor.classes().softSolid;
838
+ return 'bg-[#f0f2f5] dark:bg-[#2a2c31]';
699
839
  }
700
840
 
701
841
  /**
@@ -777,7 +917,7 @@ export class TableComponent<T = unknown> {
777
917
  : 'border-b ';
778
918
  return (
779
919
  border +
780
- '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 ' +
920
+ '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 ' +
781
921
  this.headerSoftClass() +
782
922
  ' ' +
783
923
  this.stickySurfaceClass(col, 'head') +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@africanies/angular-web-sdk",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "packageManager": "pnpm@11.24.0",
5
5
  "license": "MIT",
6
6
  "contributors": [
@@ -85,7 +85,7 @@ function parseSvg(svg, iconName) {
85
85
  */
86
86
  const PRESERVED_FILLS = new Set([
87
87
  '#ef8833',
88
- '#1cbd5d',
88
+ '#26bd5d',
89
89
  '#26a4f0',
90
90
  ]);
91
91