weg-shared-layout 0.0.24 → 0.0.25

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 (33) hide show
  1. package/dist/cjs/weg-footer_2.cjs.entry.js +218 -193
  2. package/dist/collection/components/weg-footer/weg-footer.css +97 -45
  3. package/dist/collection/components/weg-footer/weg-footer.js +22 -26
  4. package/dist/collection/components/weg-header/weg-header.css +33 -32
  5. package/dist/collection/components/weg-header/weg-header.js +142 -138
  6. package/dist/collection/styles/shared.css +3 -3
  7. package/dist/collection/utils/layout.js +57 -9
  8. package/dist/components/p-BnMIy-A5.js +1 -0
  9. package/dist/components/weg-footer.js +1 -1
  10. package/dist/components/weg-header.js +1 -1
  11. package/dist/esm/weg-footer_2.entry.js +218 -193
  12. package/dist/types/components/weg-footer/weg-footer.d.ts +2 -2
  13. package/dist/types/components/weg-header/weg-header.d.ts +14 -11
  14. package/dist/types/components.d.ts +6 -6
  15. package/dist/types/types/layout-data.d.ts +13 -27
  16. package/dist/types/utils/layout.d.ts +9 -1
  17. package/dist/weg-shared-layout/p-d74a58cf.entry.js +1 -0
  18. package/dist/weg-shared-layout/weg-shared-layout.esm.js +1 -1
  19. package/docs/angular-integration-guide.md +81 -103
  20. package/docs/angular.md +9 -27
  21. package/docs/nextjs.md +11 -10
  22. package/docs/react.md +3 -3
  23. package/docs/vanilla.md +8 -10
  24. package/package.json +1 -1
  25. package/readme.md +16 -42
  26. package/src/assets/dummy-data.json +221 -147
  27. package/dist/collection/components/weg-header/signed-in-layout.js +0 -13
  28. package/dist/collection/constants/weg-urls.js +0 -18
  29. package/dist/components/p-DbIEJ3IT.js +0 -1
  30. package/dist/types/components/weg-header/signed-in-layout.d.ts +0 -18
  31. package/dist/types/constants/weg-urls.d.ts +0 -18
  32. package/dist/weg-shared-layout/p-c6ec3888.entry.js +0 -1
  33. package/src/constants/weg-urls.ts +0 -23
@@ -51,7 +51,7 @@ Copy this list while integrating:
51
51
  | Tag | Purpose |
52
52
  | --- | --- |
53
53
  | `<weg-header>` | Header — bundled logo, CMS nav (signed out), built-in nav (signed in), Sign in / Manage Account / Sign out |
54
- | `<weg-footer>` | Footer — social links, columns, credits, copyright |
54
+ | `<weg-footer>` | Footer — social links, menu, credits, copyright |
55
55
 
56
56
  They are **presentational**: they **do not** fetch data. Your Angular app loads layout JSON and passes it in.
57
57
 
@@ -156,8 +156,6 @@ Create `src/app/auth.ts`. Host apps own sign-in/out URLs — do not import auth
156
156
 
157
157
  ```ts
158
158
  // src/app/auth.ts
159
- export const HEADER_LOGO_HREF = 'https://www.warwickemploymentgroup.com/';
160
-
161
159
  export const HEADER_SIGN_IN = {
162
160
  label: 'Sign in',
163
161
  href: 'https://account.warwickemploymentgroup.com/account/login',
@@ -167,7 +165,7 @@ export const HEADER_SIGN_IN = {
167
165
  export const ACCOUNT_LOGIN_HREF = HEADER_SIGN_IN.href;
168
166
  ```
169
167
 
170
- You will merge `HEADER_LOGO_HREF` and `HEADER_SIGN_IN` into layout data in Step 6/7.
168
+ If your API omits a Sign in entry in `header.menu`, append `HEADER_SIGN_IN` when building layout — see Step 7.
171
169
 
172
170
  ---
173
171
 
@@ -210,7 +208,7 @@ import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
210
208
  import { RouterOutlet } from '@angular/router';
211
209
  import layoutFixture from 'weg-shared-layout/dummy-data.json';
212
210
 
213
- import { ACCOUNT_LOGIN_HREF, HEADER_LOGO_HREF, HEADER_SIGN_IN } from './auth';
211
+ import { ACCOUNT_LOGIN_HREF, HEADER_SIGN_IN } from './auth';
214
212
  import type { LayoutData } from './layout.types';
215
213
 
216
214
  @Component({
@@ -221,14 +219,7 @@ import type { LayoutData } from './layout.types';
221
219
  })
222
220
  export class App {
223
221
  /** Same object for header and footer */
224
- layoutData: LayoutData = {
225
- ...layoutFixture,
226
- header: {
227
- ...layoutFixture.header,
228
- logoHref: HEADER_LOGO_HREF,
229
- signIn: HEADER_SIGN_IN,
230
- },
231
- };
222
+ layoutData: LayoutData = layoutFixture;
232
223
 
233
224
  signedIn = false;
234
225
  userName?: string;
@@ -243,8 +234,7 @@ export class App {
243
234
  return;
244
235
  }
245
236
 
246
- window.location.href =
247
- this.layoutData.header?.signIn?.href ?? HEADER_SIGN_IN.href;
237
+ window.location.href = HEADER_SIGN_IN.href;
248
238
  }
249
239
  }
250
240
  ```
@@ -287,7 +277,7 @@ import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';
287
277
  import { RouterOutlet } from '@angular/router';
288
278
  import layoutFixture from 'weg-shared-layout/dummy-data.json';
289
279
 
290
- import { ACCOUNT_LOGIN_HREF, HEADER_LOGO_HREF, HEADER_SIGN_IN } from './auth';
280
+ import { ACCOUNT_LOGIN_HREF, HEADER_SIGN_IN } from './auth';
291
281
  import type { LayoutData } from './layout.types';
292
282
 
293
283
  @Component({
@@ -297,14 +287,7 @@ import type { LayoutData } from './layout.types';
297
287
  templateUrl: './app.html',
298
288
  })
299
289
  export class App {
300
- readonly layoutData = signal<LayoutData>({
301
- ...layoutFixture,
302
- header: {
303
- ...layoutFixture.header,
304
- logoHref: HEADER_LOGO_HREF,
305
- signIn: HEADER_SIGN_IN,
306
- },
307
- });
290
+ readonly layoutData = signal<LayoutData>(layoutFixture);
308
291
 
309
292
  readonly signedIn = signal(false);
310
293
  readonly userName = signal<string | undefined>(undefined);
@@ -320,8 +303,7 @@ export class App {
320
303
  return;
321
304
  }
322
305
 
323
- window.location.href =
324
- this.layoutData().header?.signIn?.href ?? HEADER_SIGN_IN.href;
306
+ window.location.href = HEADER_SIGN_IN.href;
325
307
  }
326
308
  }
327
309
  ```
@@ -358,7 +340,7 @@ import { Injectable } from '@angular/core';
358
340
  import { HttpClient } from '@angular/common/http';
359
341
  import { map, Observable } from 'rxjs';
360
342
 
361
- import { HEADER_LOGO_HREF, HEADER_SIGN_IN } from './auth';
343
+ import { HEADER_SIGN_IN } from './auth';
362
344
  import type { LayoutData } from './layout.types';
363
345
 
364
346
  @Injectable({ providedIn: 'root' })
@@ -368,18 +350,21 @@ export class LayoutService {
368
350
  /** CMS returns full { header, footer } object */
369
351
  loadLayout(): Observable<LayoutData> {
370
352
  return this.http.get<LayoutData>('/api/layout').pipe(
371
- map((data) => this.withAppHeaderOverrides(data)),
353
+ map((data) => this.ensureSignInMenu(data)),
372
354
  );
373
355
  }
374
356
 
375
- private withAppHeaderOverrides(data: LayoutData): LayoutData {
357
+ private ensureSignInMenu(data: LayoutData): LayoutData {
358
+ const menu = data.header?.menu ?? [];
359
+ const hasSignIn = menu.some(
360
+ (item) =>
361
+ !item.items?.length &&
362
+ item.label?.trim().toLowerCase() === 'sign in',
363
+ );
364
+ if (hasSignIn) return data;
376
365
  return {
377
366
  ...data,
378
- header: {
379
- ...data.header,
380
- logoHref: HEADER_LOGO_HREF,
381
- signIn: HEADER_SIGN_IN,
382
- },
367
+ header: { ...data.header, menu: [...menu, HEADER_SIGN_IN] },
383
368
  };
384
369
  }
385
370
  }
@@ -527,14 +512,14 @@ export class App {
527
512
  | Binding | Web component | Purpose |
528
513
  | --- | --- | --- |
529
514
  | `[layout]="..."` | `layout` | CMS nav when **signed out** |
530
- | `[signedIn]="..."` | `signed-in` | `true` → built-in signed-in nav (ignores CMS dropdowns/links/signIn) |
515
+ | `[signedIn]="..."` | `signed-in` | `true` → built-in signed-in nav (ignores CMS `header.menu`) |
531
516
  | `[userName]="..."` | `user-name` | First name on Manage Account when signed in |
532
517
  | `(wegAuthClick)="..."` | `wegAuthClick` | `detail.action`: `'sign-in'` \| `'sign-out'` |
533
518
 
534
519
  **Signed out** (`signedIn === false`):
535
520
 
536
- - Uses `layout.header.dropdowns`, `links`, `signIn`
537
- - Logo links to `layout.header.logoHref` (default WEG home if omitted); logo **image** is bundled
521
+ - Uses `layout.header.menu` — dropdown groups (`items`) and flat links (`href`), including Sign in
522
+ - Logo **image** from `layout.header.logoSrc` (bundled if omitted); logo **link** always goes to WEG home
538
523
 
539
524
  **Signed in** (`signedIn === true`):
540
525
 
@@ -570,19 +555,16 @@ Both tags accept one **`layout`** object with optional `header` and `footer` key
570
555
  ### TypeScript shape
571
556
 
572
557
  ```ts
573
- type LayoutLink = { label: string; href: string };
574
-
575
- type LayoutHeaderDropdown = {
558
+ type LayoutLink = {
576
559
  label: string;
577
- items: LayoutLink[];
560
+ href?: string;
561
+ target?: '_blank' | '_self' | '_parent' | '_top';
562
+ items?: LayoutLink[];
578
563
  };
579
564
 
580
565
  type LayoutHeaderData = {
581
- logoHref?: string;
582
- dropdowns?: LayoutHeaderDropdown[];
583
- links?: LayoutLink[];
584
- signIn?: LayoutLink;
585
- signOut?: { label: string; href?: string };
566
+ logoSrc?: string;
567
+ menu?: LayoutLink[];
586
568
  };
587
569
 
588
570
  type LayoutFooterSocialLink = {
@@ -590,18 +572,16 @@ type LayoutFooterSocialLink = {
590
572
  href: string;
591
573
  };
592
574
 
593
- type LayoutFooterColumn = {
594
- links: LayoutLink[];
575
+ type LayoutFooterData = {
576
+ social?: LayoutFooterSocialLink[];
577
+ menu?: LayoutLink[][];
578
+ credits?: string;
579
+ copyright?: string;
595
580
  };
596
581
 
597
582
  type LayoutData = {
598
583
  header?: Partial<LayoutHeaderData>;
599
- footer?: Partial<{
600
- social: LayoutFooterSocialLink[];
601
- columns: LayoutFooterColumn[];
602
- credits: string;
603
- copyright: string;
604
- }>;
584
+ footer?: Partial<LayoutFooterData>;
605
585
  };
606
586
  ```
607
587
 
@@ -616,27 +596,25 @@ Full example JSON:
616
596
  ```json
617
597
  {
618
598
  "header": {
619
- "logoHref": "https://www.warwickemploymentgroup.com/",
620
- "dropdowns": [
599
+ "logoSrc": "https://www.warwickemploymentgroup.com/assets/weg-logo.svg",
600
+ "menu": [
621
601
  {
622
602
  "label": "Find a job",
623
603
  "items": [
624
604
  { "label": "Graduates", "href": "https://example.com/jobs/graduates" }
625
605
  ]
626
- }
627
- ],
628
- "links": [
606
+ },
629
607
  { "label": "Career advice", "href": "https://example.com/career-advice" },
630
- { "label": "Register", "href": "https://example.com/register" }
631
- ],
632
- "signIn": { "label": "Sign in", "href": "https://example.com/login" }
608
+ { "label": "Register", "href": "https://example.com/register" },
609
+ { "label": "Sign in", "href": "https://example.com/login" }
610
+ ]
633
611
  },
634
612
  "footer": {
635
613
  "social": [
636
614
  { "platform": "LinkedIn", "href": "https://linkedin.com/company/example" }
637
615
  ],
638
- "columns": [
639
- { "links": [{ "label": "About WEG", "href": "/about" }] }
616
+ "menu": [
617
+ [{ "label": "About WEG", "href": "/about" }]
640
618
  ],
641
619
  "credits": "Warwick Employment Group …",
642
620
  "copyright": "Copyright © Warwick Employment Group."
@@ -648,17 +626,14 @@ Full example JSON:
648
626
 
649
627
  | Field | Used when | Notes |
650
628
  | --- | --- | --- |
651
- | `header.logoHref` | Signed **out** | Logo link; image is bundled |
652
- | `header.dropdowns` | Signed **out** | `{ label, items[] }` |
653
- | `header.links` | Signed **out** | Flat nav links |
654
- | `header.signIn` | Signed **out** | Sign in button |
655
- | `header.signOut` | Rarely | Signed-in sign-out uses built-in URLs |
629
+ | `header.logoSrc` | Signed **out** | Logo image URL; bundled if omitted |
630
+ | `header.menu` | Signed **out** | Unified nav — groups use `items[]`, flat links use `href` |
656
631
  | `footer.social` | Always | `platform` must be `LinkedIn`, `Instagram`, `TikTok`, or `YouTube` exactly |
657
- | `footer.columns` | Always | `{ links: [...] }[]` |
658
- | `footer.credits` | Always | Text below columns |
632
+ | `footer.menu` | Always | `LayoutLink[][]` — one link array per column |
633
+ | `footer.credits` | Always | Text below menu |
659
634
  | `footer.copyright` | Always | Copyright line |
660
635
 
661
- When **`signedIn` is true**, `<weg-header>` **ignores** `dropdowns`, `links`, and `signIn` from layout.
636
+ When **`signedIn` is true**, `<weg-header>` **ignores** `header.menu` from layout.
662
637
 
663
638
  ### Property binding vs attributes
664
639
 
@@ -675,7 +650,7 @@ The component can parse a JSON **string** for `layout`, but `[layout]="object"`
675
650
  ### Invalid entries are silently dropped
676
651
 
677
652
  - Links without `label` or `href` are skipped
678
- - Dropdowns with empty `items` are skipped
653
+ - Menu groups with empty `items` are skipped
679
654
  - Unknown social `platform` values are skipped
680
655
  - Malformed input → empty sections (no throw) — inspect `$0.layout` in DevTools
681
656
 
@@ -703,19 +678,18 @@ const navLinks = [
703
678
 
704
679
  | Field | Array element shape |
705
680
  | --- | --- |
706
- | `header.dropdowns` | `{ label, items: { label, href }[] }` |
707
- | `header.links` | `{ label, href }` |
681
+ | `header.menu` | `LayoutLink` — group with `items[]` or flat link with `href` |
708
682
  | `footer.social` | `{ platform, href }` |
709
- | `footer.columns` | `{ links: { label, href }[] }` |
683
+ | `footer.menu` | `{ label, href }[]` per column |
710
684
 
711
- **`src/app/layout.service.ts` — merge multiple endpoints:**
685
+ **`src/app/layout.service.ts` — merge multiple endpoints into one menu:**
712
686
 
713
687
  ```ts
714
688
  import { Injectable } from '@angular/core';
715
689
  import { HttpClient } from '@angular/common/http';
716
690
  import { forkJoin, map, Observable } from 'rxjs';
717
691
 
718
- import { HEADER_LOGO_HREF, HEADER_SIGN_IN } from './auth';
692
+ import { HEADER_SIGN_IN } from './auth';
719
693
  import type { LayoutData } from './layout.types';
720
694
 
721
695
  @Injectable({ providedIn: 'root' })
@@ -724,18 +698,17 @@ export class LayoutService {
724
698
 
725
699
  loadLayoutFromParts(): Observable<LayoutData> {
726
700
  return forkJoin({
727
- dropdowns: this.http.get<NonNullable<LayoutData['header']>['dropdowns']>(
728
- '/api/nav/dropdowns',
701
+ menuGroups: this.http.get<NonNullable<LayoutData['header']>['menu']>(
702
+ '/api/nav/menu',
703
+ ),
704
+ flatLinks: this.http.get<NonNullable<LayoutData['header']>['menu']>(
705
+ '/api/nav/links',
729
706
  ),
730
- links: this.http.get<NonNullable<LayoutData['header']>['links']>('/api/nav/links'),
731
707
  footer: this.http.get<LayoutData['footer']>('/api/footer'),
732
708
  }).pipe(
733
- map(({ dropdowns, links, footer }) => ({
709
+ map(({ menuGroups, flatLinks, footer }) => ({
734
710
  header: {
735
- logoHref: HEADER_LOGO_HREF,
736
- dropdowns,
737
- links,
738
- signIn: HEADER_SIGN_IN,
711
+ menu: [...menuGroups, ...flatLinks, HEADER_SIGN_IN],
739
712
  },
740
713
  footer,
741
714
  })),
@@ -771,29 +744,30 @@ import { Injectable } from '@angular/core';
771
744
  import { BehaviorSubject } from 'rxjs';
772
745
  import layoutFixture from 'weg-shared-layout/dummy-data.json';
773
746
 
774
- import { HEADER_LOGO_HREF, HEADER_SIGN_IN } from './auth';
747
+ import { HEADER_SIGN_IN } from './auth';
775
748
  import type { LayoutData } from './layout.types';
776
749
 
777
750
  @Injectable({ providedIn: 'root' })
778
751
  export class LayoutService {
779
- private readonly layoutSubject = new BehaviorSubject(
780
- this.withAppHeaderOverrides(layoutFixture),
781
- );
752
+ private readonly layoutSubject = new BehaviorSubject<LayoutData>(layoutFixture);
782
753
 
783
754
  readonly layout$ = this.layoutSubject.asObservable();
784
755
 
785
756
  setLayout(data: LayoutData): void {
786
- this.layoutSubject.next(this.withAppHeaderOverrides(data));
757
+ this.layoutSubject.next(this.ensureSignInMenu(data));
787
758
  }
788
759
 
789
- private withAppHeaderOverrides(data: LayoutData): LayoutData {
760
+ private ensureSignInMenu(data: LayoutData): LayoutData {
761
+ const menu = data.header?.menu ?? [];
762
+ const hasSignIn = menu.some(
763
+ (item) =>
764
+ !item.items?.length &&
765
+ item.label?.trim().toLowerCase() === 'sign in',
766
+ );
767
+ if (hasSignIn) return data;
790
768
  return {
791
769
  ...data,
792
- header: {
793
- ...data.header,
794
- logoHref: HEADER_LOGO_HREF,
795
- signIn: HEADER_SIGN_IN,
796
- },
770
+ header: { ...data.header, menu: [...menu, HEADER_SIGN_IN] },
797
771
  };
798
772
  }
799
773
  }
@@ -809,11 +783,11 @@ Stencil watches the `layout` **property**. Assign a **new object reference** whe
809
783
  // Good
810
784
  this.layoutData = {
811
785
  ...this.layoutData,
812
- header: { ...this.layoutData.header, links: newLinks },
786
+ header: { ...this.layoutData.header, menu: newMenu },
813
787
  };
814
788
 
815
789
  // Risky — may not re-render
816
- this.layoutData.header!.links!.push(newLink);
790
+ this.layoutData.header!.menu!.push(newLink);
817
791
  ```
818
792
 
819
793
  With `OnPush` and plain fields, call `ChangeDetectorRef.markForCheck()` after updates.
@@ -926,7 +900,11 @@ const header = fixture.nativeElement.querySelector('weg-header') as HTMLElement
926
900
  layout?: LayoutData;
927
901
  };
928
902
 
929
- expect(header.layout?.header?.signIn).toEqual(HEADER_SIGN_IN);
903
+ expect(
904
+ header.layout?.header?.menu?.some(
905
+ (item) => item.label === 'Sign in' && item.href === HEADER_SIGN_IN.href,
906
+ ),
907
+ ).toBe(true);
930
908
  ```
931
909
 
932
910
  Full example: [weg-angular-demo `app.spec.ts`](https://github.com/jobsac/weg-angular-demo/blob/main/src/app/app.spec.ts).
@@ -968,7 +946,7 @@ External `https://` links open in a new tab automatically.
968
946
  | `'weg-header' is not a known element` | Missing schema | `CUSTOM_ELEMENTS_SCHEMA` on the component that owns the template |
969
947
  | Header/footer completely empty | Not registered | `defineCustomElements()` before bootstrap, or tag side-effect imports |
970
948
  | Tags render, nav empty | Wrong `layout` | `[layout]="..."` with object `{ header?, footer? }` |
971
- | Passed array to `[layout]` | Top-level must be object | `{ header: { links: myArray } }` — see [Optional: API returns separate arrays](#optional-api-returns-separate-arrays-not-one-layout-object) |
949
+ | Passed array to `[layout]` | Top-level must be object | `{ header: { menu: myArray } }` — see [Optional: API returns separate arrays](#optional-api-returns-separate-arrays-not-one-layout-object) |
972
950
  | Auth always Sign in | `signedIn` not bound / false | Bind session; set `true` when logged in |
973
951
  | Generic Manage Account | `userName` missing | Pass first name when `signedIn` is true |
974
952
  | Layout changes ignored | In-place mutation | New object reference |
@@ -976,7 +954,7 @@ External `https://` links open in a new tab automatically.
976
954
  | Tests fail | Elements undefined | `defineCustomElements()` in `beforeAll` |
977
955
  | SSR `document is not defined` | Loader on server | `typeof window !== 'undefined'` guard |
978
956
  | Missing social icons | Bad `platform` | Exactly `LinkedIn`, `Instagram`, `TikTok`, `YouTube` |
979
- | Missing dropdown | Normalization | Non-empty `label` and valid `items` |
957
+ | Missing menu item | Normalization | Non-empty `label` and valid `items` or `href` |
980
958
 
981
959
  ### DevTools
982
960
 
@@ -996,7 +974,7 @@ $0.userName
996
974
  | --- | --- |
997
975
  | `src/main.ts` | `defineCustomElements()` then bootstrap |
998
976
  | `tsconfig.app.json` | `resolveJsonModule: true` |
999
- | `src/app/auth.ts` | `HEADER_SIGN_IN`, `ACCOUNT_LOGIN_HREF`, `HEADER_LOGO_HREF` |
977
+ | `src/app/auth.ts` | `HEADER_SIGN_IN`, `ACCOUNT_LOGIN_HREF` |
1000
978
  | `src/app/layout.types.ts` | `export type LayoutData = typeof layoutFixture` |
1001
979
  | `src/app/layout.service.ts` | HTTP / API mapping (Step 7, optional arrays) |
1002
980
  | `src/app/app.ts` | Shell: schema, layout, auth handler |
package/docs/angular.md CHANGED
@@ -19,7 +19,7 @@ Reference demo: [weg-angular-demo](https://github.com/jobsac/weg-angular-demo).
19
19
  | Tag | Purpose |
20
20
  | --- | --- |
21
21
  | `<weg-header>` | Header — CMS nav (signed out), built-in nav (signed in), auth |
22
- | `<weg-footer>` | Footer — social, columns, credits, copyright |
22
+ | `<weg-footer>` | Footer — social, menu, credits, copyright |
23
23
 
24
24
  Both accept the same `layout` payload ([`dummy-data.json`](../src/assets/dummy-data.json) or your API). `<weg-header>` also accepts **`signed-in`**, **`user-name`**, and emits **`wegAuthClick`**.
25
25
 
@@ -60,8 +60,6 @@ Add `schemas: [CUSTOM_ELEMENTS_SCHEMA]` to every `@Component` (or `@NgModule`) w
60
60
 
61
61
  ```ts
62
62
  // src/app/auth.ts
63
- export const HEADER_LOGO_HREF = 'https://www.warwickemploymentgroup.com/';
64
-
65
63
  export const HEADER_SIGN_IN = {
66
64
  label: 'Sign in',
67
65
  href: 'https://account.warwickemploymentgroup.com/account/login',
@@ -89,7 +87,7 @@ import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
89
87
  import { RouterOutlet } from '@angular/router';
90
88
  import layoutFixture from 'weg-shared-layout/dummy-data.json';
91
89
 
92
- import { ACCOUNT_LOGIN_HREF, HEADER_LOGO_HREF, HEADER_SIGN_IN } from './auth';
90
+ import { ACCOUNT_LOGIN_HREF, HEADER_SIGN_IN } from './auth';
93
91
 
94
92
  @Component({
95
93
  selector: 'app-root',
@@ -98,14 +96,7 @@ import { ACCOUNT_LOGIN_HREF, HEADER_LOGO_HREF, HEADER_SIGN_IN } from './auth';
98
96
  templateUrl: './app.html',
99
97
  })
100
98
  export class App {
101
- layoutData = {
102
- ...layoutFixture,
103
- header: {
104
- ...layoutFixture.header,
105
- logoHref: HEADER_LOGO_HREF,
106
- signIn: HEADER_SIGN_IN,
107
- },
108
- };
99
+ layoutData = layoutFixture;
109
100
 
110
101
  signedIn = false;
111
102
  userName?: string;
@@ -120,8 +111,7 @@ export class App {
120
111
  return;
121
112
  }
122
113
 
123
- window.location.href =
124
- this.layoutData.header?.signIn?.href ?? HEADER_SIGN_IN.href;
114
+ window.location.href = HEADER_SIGN_IN.href;
125
115
  }
126
116
  }
127
117
  ```
@@ -152,7 +142,7 @@ import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';
152
142
  import { RouterOutlet } from '@angular/router';
153
143
  import layoutFixture from 'weg-shared-layout/dummy-data.json';
154
144
 
155
- import { ACCOUNT_LOGIN_HREF, HEADER_LOGO_HREF, HEADER_SIGN_IN } from './auth';
145
+ import { ACCOUNT_LOGIN_HREF, HEADER_SIGN_IN } from './auth';
156
146
  import type { LayoutData } from './layout.types';
157
147
 
158
148
  @Component({
@@ -162,14 +152,7 @@ import type { LayoutData } from './layout.types';
162
152
  templateUrl: './app.html',
163
153
  })
164
154
  export class App {
165
- readonly layoutData = signal<LayoutData>({
166
- ...layoutFixture,
167
- header: {
168
- ...layoutFixture.header,
169
- logoHref: HEADER_LOGO_HREF,
170
- signIn: HEADER_SIGN_IN,
171
- },
172
- });
155
+ readonly layoutData = signal<LayoutData>(layoutFixture);
173
156
 
174
157
  readonly signedIn = signal(false);
175
158
  readonly userName = signal<string | undefined>(undefined);
@@ -185,8 +168,7 @@ export class App {
185
168
  return;
186
169
  }
187
170
 
188
- window.location.href =
189
- this.layoutData().header?.signIn?.href ?? HEADER_SIGN_IN.href;
171
+ window.location.href = HEADER_SIGN_IN.href;
190
172
  }
191
173
  }
192
174
  ```
@@ -211,7 +193,7 @@ HTTP with signals: **[Step 7B](./angular-integration-guide.md#step-7b--http-with
211
193
 
212
194
  | Input / output | Binding (5A) | Binding (5B) | Notes |
213
195
  | --- | --- | --- | --- |
214
- | CMS nav | `[layout]="layoutData"` | `[layout]="layoutData()"` | `dropdowns`, `links`, `signIn` when signed out |
196
+ | CMS nav | `[layout]="layoutData"` | `[layout]="layoutData()"` | `header.menu` — groups (`items`) and flat links (`href`) when signed out |
215
197
  | Session state | `[signedIn]="signedIn"` | `[signedIn]="signedIn()"` | `true` → built-in signed-in nav |
216
198
  | User name | `[userName]="userName"` | `[userName]="userName()"` | Manage Account label when signed in |
217
199
  | Auth click | `(wegAuthClick)="onAuthClick($event)"` | `detail.action`: `'sign-in'` \| `'sign-out'` |
@@ -219,7 +201,7 @@ HTTP with signals: **[Step 7B](./angular-integration-guide.md#step-7b--http-with
219
201
 
220
202
  **Signed-in nav (built in):** Find a job, Dashboard, Manage Account, Sign out.
221
203
 
222
- Logo **image** is bundled. Logo **link** uses `layout.header.logoHref` when signed out.
204
+ Logo **image** uses `layout.header.logoSrc` (bundled if omitted). Logo **link** always goes to WEG home.
223
205
 
224
206
  ## Footer
225
207
 
package/docs/nextjs.md CHANGED
@@ -7,7 +7,7 @@ Guide for **Next.js 13+ App Router** (`app/` directory). For Vite/CRA-style clie
7
7
  | Tag | Purpose |
8
8
  | --- | --- |
9
9
  | `<weg-header>` | Site header — bundled logo, CMS nav (signed out), built-in nav (signed in), Sign in / Manage Account / Sign out |
10
- | `<weg-footer>` | Site footer — social, columns, legal text |
10
+ | `<weg-footer>` | Site footer — social, menu, legal text |
11
11
 
12
12
  Both accept **`layout`** (JSON string recommended in Next). `<weg-header>` also accepts **`signed-in`**, **`user-name`**, and emits **`wegAuthClick`**.
13
13
 
@@ -79,10 +79,12 @@ defineCustomElements();
79
79
 
80
80
  type LayoutData = {
81
81
  header?: {
82
- logoHref?: string;
83
- dropdowns?: { label: string; items: { label: string; href: string }[] }[];
84
- links?: { label: string; href: string }[];
85
- signIn?: { label: string; href: string };
82
+ logoSrc?: string;
83
+ menu?: {
84
+ label: string;
85
+ href?: string;
86
+ items?: { label: string; href: string }[];
87
+ }[];
86
88
  };
87
89
  };
88
90
 
@@ -107,7 +109,7 @@ export function Header({
107
109
  return;
108
110
  }
109
111
 
110
- window.location.href = layout.header?.signIn?.href ?? HEADER_SIGN_IN.href;
112
+ window.location.href = HEADER_SIGN_IN.href;
111
113
  },
112
114
  [layout, onSignedInChange],
113
115
  );
@@ -234,9 +236,8 @@ Wire **`signedIn`** and **`userName`** from your auth provider (session hook / c
234
236
 
235
237
  | API | Signed out | Signed in |
236
238
  | --- | --- | --- |
237
- | `layout.header.dropdowns` / `links` | CMS nav rendered | Ignored — built-in nav used |
238
- | `layout.header.signIn` | Sign in button | Not shown |
239
- | `layout.header.logoHref` | Logo link target | Built-in WEG home URL |
239
+ | `layout.header.menu` | CMS nav rendered (groups + flat links incl. Sign in) | Ignored — built-in nav used |
240
+ | `layout.header.logoSrc` | Logo image URL (bundled if omitted) | Built-in bundled logo |
240
241
  | `signed-in` prop | `false` | `true` — session flag from host app |
241
242
  | `user-name` prop | — | User's first name on Manage Account |
242
243
  | `wegAuthClick` event | `'sign-in'` on Sign in click | `'sign-out'` on Sign out click |
@@ -244,7 +245,7 @@ Wire **`signedIn`** and **`userName`** from your auth provider (session hook / c
244
245
 
245
246
  **Signed-in nav (built into the component):** Find a job, Dashboard, Manage Account, Sign out.
246
247
 
247
- The logo **image** is bundled. The logo **link** uses `layout.header.logoHref` when signed out.
248
+ The logo **image** uses `layout.header.logoSrc` when signed out (bundled if omitted). The logo **link** always goes to WEG home.
248
249
 
249
250
  ## Passing `layout` — quick reference
250
251
 
package/docs/react.md CHANGED
@@ -7,7 +7,7 @@ Guide for **client-rendered** React apps (Vite, Create React App, etc.). If you
7
7
  | Tag | Purpose |
8
8
  | --- | --- |
9
9
  | `<weg-header>` | Site header — bundled logo, CMS nav (signed out), built-in nav (signed in), Sign in / Manage Account / Sign out |
10
- | `<weg-footer>` | Site footer — social links, columns, credits, copyright |
10
+ | `<weg-footer>` | Site footer — social links, menu, credits, copyright |
11
11
 
12
12
  Both are **presentational** [Stencil](https://stenciljs.com/) Web Components. They **do not fetch data** — your app passes a **`layout`** payload (API, CMS, or [`dummy-data.json`](../src/assets/dummy-data.json)).
13
13
 
@@ -89,7 +89,7 @@ export const ACCOUNT_LOGIN_HREF = HEADER_SIGN_IN.href;
89
89
 
90
90
  ### Signed out
91
91
 
92
- Pass CMS/API layout with `dropdowns`, `links`, and `signIn`. Use [`dummy-data.json`](../src/assets/dummy-data.json) for the full shape. Keep auth URLs in a local file:
92
+ Pass CMS/API layout with unified `header.menu`. Use [`dummy-data.json`](../src/assets/dummy-data.json) for the full shape — dropdown groups use `items`, flat links and Sign in use `href`. Keep auth URLs in a local file:
93
93
 
94
94
  ```ts
95
95
  // auth.ts (host app)
@@ -124,7 +124,7 @@ export function SiteHeader() {
124
124
  return;
125
125
  }
126
126
 
127
- window.location.href = layout.header.signIn?.href ?? HEADER_SIGN_IN.href;
127
+ window.location.href = HEADER_SIGN_IN.href;
128
128
  }, []);
129
129
 
130
130
  return (
package/docs/vanilla.md CHANGED
@@ -5,7 +5,7 @@
5
5
  | Tag | Purpose |
6
6
  | --- | --- |
7
7
  | `<weg-header>` | Site header — bundled logo, CMS nav (signed out), built-in nav (signed in), Sign in / Manage Account / Sign out |
8
- | `<weg-footer>` | Site footer — social links, columns, credits, copyright |
8
+ | `<weg-footer>` | Site footer — social links, menu, credits, copyright |
9
9
 
10
10
  Both components are **presentational**: they do **not** fetch data. Pass the same `layout` payload to each.
11
11
 
@@ -53,17 +53,15 @@ element.layout = layoutObject;
53
53
  Or pass a JSON **string** on the `layout` attribute:
54
54
 
55
55
  ```html
56
- <weg-header layout='{"header":{"links":[]}}'></weg-header>
56
+ <weg-header layout='{"header":{"menu":[]}}'></weg-header>
57
57
  ```
58
58
 
59
59
  ## Header layout fields (signed out)
60
60
 
61
61
  | Field | Purpose |
62
62
  | --- | --- |
63
- | `header.logoHref` | Logo link target (defaults to WEG home) |
64
- | `header.dropdowns` | Dropdown menus from CMS |
65
- | `header.links` | Flat nav links from CMS |
66
- | `header.signIn` | Sign in button `{ label, href }` |
63
+ | `header.logoSrc` | Logo image URL (bundled if omitted) |
64
+ | `header.menu` | Unified nav — groups with `items[]`, flat links with `href` (incl. Sign in) |
67
65
 
68
66
  See [`dummy-data.json`](../src/assets/dummy-data.json) for production URL examples.
69
67
 
@@ -82,7 +80,7 @@ export const ACCOUNT_LOGIN_HREF = HEADER_SIGN_IN.href;
82
80
 
83
81
  ### Signed out
84
82
 
85
- Renders `dropdowns`, `links`, and `signIn` from `layout`.
83
+ Renders `header.menu` from `layout` — dropdown groups, flat links, and the Sign in entry (label **Sign in** fires `wegAuthClick`).
86
84
 
87
85
  ### Signed in
88
86
 
@@ -110,16 +108,16 @@ header.addEventListener('wegAuthClick', (event) => {
110
108
  return;
111
109
  }
112
110
 
113
- window.location.href = layout.header.signIn?.href ?? HEADER_SIGN_IN.href;
111
+ window.location.href = HEADER_SIGN_IN.href;
114
112
  });
115
113
  ```
116
114
 
117
115
  | `event.detail.action` | Default behaviour if not prevented |
118
116
  | --- | --- |
119
- | `'sign-in'` | Browser follows `signIn.href` |
117
+ | `'sign-in'` | Browser follows the Sign in menu entry's `href` |
120
118
  | `'sign-out'` | Redirects to built-in sign-out URL |
121
119
 
122
- Logo **image** is bundled. Logo **link** uses `layout.header.logoHref` when signed out.
120
+ Logo **image** uses `header.logoSrc` (bundled if omitted). Logo **link** always goes to WEG home.
123
121
 
124
122
  ## Without a bundler
125
123
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "weg-shared-layout",
3
- "version": "0.0.24",
3
+ "version": "0.0.25",
4
4
  "description": "Shared layout Web Components built with Stencil",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.js",