weg-shared-layout 0.0.14 → 0.0.15

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 (50) hide show
  1. package/dist/cjs/{index-CmiaQ_Dj.js → index-CC8e1qRm.js} +112 -0
  2. package/dist/cjs/loader.cjs.js +2 -2
  3. package/dist/cjs/my-component.cjs.entry.js +1 -1
  4. package/dist/cjs/weg-footer_2.cjs.entry.js +438 -0
  5. package/dist/cjs/weg-shared-layout.cjs.js +2 -2
  6. package/dist/collection/collection-manifest.json +2 -1
  7. package/dist/collection/components/weg-footer/weg-footer.css +1 -24
  8. package/dist/collection/components/weg-footer/weg-footer.js +5 -40
  9. package/dist/collection/components/weg-header/logo-data.js +2 -0
  10. package/dist/collection/components/weg-header/weg-header.cmp.test.js +77 -0
  11. package/dist/collection/components/weg-header/weg-header.css +327 -0
  12. package/dist/collection/components/weg-header/weg-header.js +344 -0
  13. package/dist/collection/styles/shared.css +49 -0
  14. package/dist/collection/utils/layout.js +33 -0
  15. package/dist/collection/utils/layout.unit.test.js +36 -0
  16. package/dist/components/index.js +1 -1
  17. package/dist/components/my-component.js +1 -1
  18. package/dist/components/p-CtYuWNO6.js +1 -0
  19. package/dist/components/p-DbIEJ3IT.js +1 -0
  20. package/dist/components/weg-footer.js +1 -1
  21. package/dist/components/weg-header.d.ts +11 -0
  22. package/dist/components/weg-header.js +1 -0
  23. package/dist/esm/{index-QiJxC4Ow.js → index-D8pmhPiH.js} +111 -1
  24. package/dist/esm/loader.js +3 -3
  25. package/dist/esm/my-component.entry.js +1 -1
  26. package/dist/esm/weg-footer_2.entry.js +435 -0
  27. package/dist/esm/weg-shared-layout.js +3 -3
  28. package/dist/types/components/weg-header/logo-data.d.ts +2 -0
  29. package/dist/types/components/weg-header/weg-header.cmp.test.d.ts +1 -0
  30. package/dist/types/components/weg-header/weg-header.d.ts +56 -0
  31. package/dist/types/components.d.ts +56 -2
  32. package/dist/types/types/layout-data.d.ts +21 -4
  33. package/dist/types/utils/layout.d.ts +4 -0
  34. package/dist/types/utils/layout.unit.test.d.ts +1 -0
  35. package/dist/weg-shared-layout/p-D8pmhPiH.js +2 -0
  36. package/dist/weg-shared-layout/{p-d1addb13.entry.js → p-d61033bd.entry.js} +1 -1
  37. package/dist/weg-shared-layout/p-eaf953a4.entry.js +1 -0
  38. package/dist/weg-shared-layout/weg-shared-layout.esm.js +1 -1
  39. package/docs/angular.md +66 -18
  40. package/docs/nextjs.md +145 -111
  41. package/docs/react.md +123 -59
  42. package/docs/vanilla.md +99 -3
  43. package/package.json +5 -1
  44. package/readme.md +31 -3
  45. package/src/assets/dummy-data.json +41 -1
  46. package/dist/cjs/weg-footer.cjs.entry.js +0 -189
  47. package/dist/components/p-BTQYW5OR.js +0 -1
  48. package/dist/esm/weg-footer.entry.js +0 -187
  49. package/dist/weg-shared-layout/p-0c28f34f.entry.js +0 -1
  50. package/dist/weg-shared-layout/p-QiJxC4Ow.js +0 -2
package/docs/nextjs.md CHANGED
@@ -2,25 +2,32 @@
2
2
 
3
3
  Guide for **Next.js 13+ App Router** (`app/` directory). For Vite/CRA-style client-only React, see **[React SPA](./react.md)**.
4
4
 
5
+ ## Components
6
+
7
+ | Tag | Purpose |
8
+ | --- | --- |
9
+ | `<weg-header>` | Site header — logo (bundled), nav, Sign in / Sign out |
10
+ | `<weg-footer>` | Site footer — social, columns, legal text |
11
+
12
+ Both accept **`layout`** (JSON string recommended in Next). `<weg-header>` also accepts **`signed-in`** and emits **`wegAuthClick`**.
13
+
5
14
  ## Why Next.js is different
6
15
 
7
16
  | Topic | SPA (React 19) | Next.js App Router |
8
17
  | --- | --- | --- |
9
- | **Where `<weg-footer>` runs** | Client only | Must be in a **Client Component** (`"use client"`) |
10
- | **Server Components** | N/A | Default in `app/` — cannot call `defineCustomElements()` or touch `window` |
11
- | **SSR output** | N/A | Server HTML is often an **empty** `<weg-footer></weg-footer>` until the client bundle hydrates |
12
- | **`layout={object}`** | Works on React 19+ | **Does not work reliably** — RSC/SSR serializes props; object props on custom elements become useless attributes |
13
- | **Recommended `layout` value** | Object (React 19+) or string | **`JSON.stringify(layout)`** always |
14
-
15
- Stencil custom elements need browser APIs. Registration and rendering belong in a small client wrapper; the root **Server** layout only imports that wrapper.
18
+ | **Where components run** | Client only | Must be in **Client Components** (`"use client"`) |
19
+ | **Server Components** | N/A | Default in `app/` — cannot call `defineCustomElements()` |
20
+ | **SSR output** | N/A | Often empty custom-element tags until client hydrates |
21
+ | **`layout={object}` on CE** | Works on React 19+ | **Unreliable** — use **`JSON.stringify(layout)`** |
22
+ | **Auth state** | `signed-in` prop | Set in client wrapper from session hook / context |
16
23
 
17
24
  ## Requirements
18
25
 
19
26
  | Requirement | Notes |
20
27
  | --- | --- |
21
28
  | **Next.js 13+** | App Router (`app/layout.tsx`) |
22
- | **React 19** | Matches current Next defaults; string `layout` still required for custom elements |
23
- | **`transpilePackages`** | Next must compile `weg-shared-layout` (see below) |
29
+ | **`transpilePackages`** | Next must compile `weg-shared-layout` |
30
+ | **`suppressHydrationWarning`** | Recommended on both tags (SSR placeholder vs client shadow DOM) |
24
31
 
25
32
  ## Install
26
33
 
@@ -32,9 +39,8 @@ pnpm add weg-shared-layout
32
39
 
33
40
  ## 1. Transpile the package
34
41
 
35
- In `next.config.ts` / `next.config.js`:
36
-
37
42
  ```ts
43
+ // next.config.ts
38
44
  import type { NextConfig } from 'next';
39
45
 
40
46
  const nextConfig: NextConfig = {
@@ -44,66 +50,137 @@ const nextConfig: NextConfig = {
44
50
  export default nextConfig;
45
51
  ```
46
52
 
47
- Without this, you may see build errors or stale/uncompiled Stencil output in the Next bundle.
53
+ ## 2. Client layout components
54
+
55
+ ```tsx
56
+ // src/components/layout/Header.tsx
57
+ 'use client';
58
+
59
+ import { useCallback } from 'react';
60
+ import { defineCustomElements } from 'weg-shared-layout/loader';
61
+ import 'weg-shared-layout/weg-header';
48
62
 
49
- ## 2. Client `Footer` component
63
+ defineCustomElements();
50
64
 
51
- Create a dedicated Client Component (pattern verified in [weg-payload](https://github.com/jobsac/weg-payload)):
65
+ type LayoutData = {
66
+ header?: {
67
+ dropdowns?: { label: string; items: { label: string; href: string }[] }[];
68
+ links?: { label: string; href: string }[];
69
+ signIn?: { label: string; href: string };
70
+ signOut?: { label: string; href?: string };
71
+ };
72
+ };
73
+
74
+ export function Header({
75
+ layout,
76
+ signedIn,
77
+ onSignedInChange,
78
+ }: {
79
+ layout: LayoutData;
80
+ signedIn: boolean;
81
+ onSignedInChange?: (signedIn: boolean) => void;
82
+ }) {
83
+ const onAuthClick = useCallback(
84
+ (event: CustomEvent<{ action: 'sign-in' | 'sign-out' }>) => {
85
+ event.preventDefault();
86
+
87
+ if (event.detail.action === 'sign-out') {
88
+ // your logout(), then:
89
+ onSignedInChange?.(false);
90
+ return;
91
+ }
92
+
93
+ window.location.href = layout.header?.signIn?.href ?? '/account/login';
94
+ },
95
+ [layout, onSignedInChange],
96
+ );
97
+
98
+ return (
99
+ <weg-header
100
+ layout={JSON.stringify(layout)}
101
+ signed-in={signedIn}
102
+ suppressHydrationWarning
103
+ // @ts-expect-error Stencil custom event
104
+ onWegAuthClick={onAuthClick}
105
+ />
106
+ );
107
+ }
108
+ ```
52
109
 
53
110
  ```tsx
54
111
  // src/components/layout/Footer.tsx
55
112
  'use client';
56
113
 
57
- import layout from 'weg-shared-layout/dummy-data.json';
58
114
  import { defineCustomElements } from 'weg-shared-layout/loader';
59
115
  import 'weg-shared-layout/weg-footer';
60
116
 
61
117
  defineCustomElements();
62
118
 
63
- export function Footer() {
119
+ export function Footer({ layout }: { layout: unknown }) {
120
+ return <weg-footer layout={JSON.stringify(layout)} suppressHydrationWarning />;
121
+ }
122
+ ```
123
+
124
+ ```tsx
125
+ // src/components/layout/SiteChrome.tsx — optional wrapper
126
+ 'use client';
127
+
128
+ import { useState } from 'react';
129
+ import { Header } from './Header';
130
+ import { Footer } from './Footer';
131
+
132
+ export function SiteChrome({
133
+ layout,
134
+ children,
135
+ }: {
136
+ layout: unknown;
137
+ children: React.ReactNode;
138
+ }) {
139
+ const [signedIn, setSignedIn] = useState(false);
140
+
64
141
  return (
65
- <weg-footer layout={JSON.stringify(layout)} suppressHydrationWarning />
142
+ <>
143
+ <Header layout={layout} signedIn={signedIn} onSignedInChange={setSignedIn} />
144
+ {children}
145
+ <Footer layout={layout} />
146
+ </>
66
147
  );
67
148
  }
68
149
  ```
69
150
 
70
- ### Why each line matters
151
+ ### Why `JSON.stringify` and `suppressHydrationWarning`
71
152
 
72
153
  | Line | Purpose |
73
154
  | --- | --- |
74
- | `'use client'` | Allows `defineCustomElements`, custom element upgrade, and DOM property updates |
75
- | `defineCustomElements()` | Registers `<weg-footer>` in the browser (module runs once per client bundle) |
76
- | `import 'weg-shared-layout/weg-footer'` | Ensures the component definition is bundled |
77
- | `layout={JSON.stringify(layout)}` | Sets a string attribute/property Next/React can pass through SSR → client; component parses JSON |
78
- | `suppressHydrationWarning` | Server renders empty tag; client fills content — avoids React hydration mismatch noise |
155
+ | `layout={JSON.stringify(layout)}` | Serializable across RSC client; component parses JSON |
156
+ | `suppressHydrationWarning` | Server renders empty tag; client fills shadow DOM |
157
+ | `'use client'` | Registration and auth handlers need the browser |
79
158
 
80
- Do **not** pass `layout={layoutObject}` in Next it will not hydrate correctly.
159
+ Do **not** pass `layout={layoutObject}` directly on the custom elements in Next.
81
160
 
82
- ## 3. Server layout: import the client footer
161
+ ## 3. Server layout
83
162
 
84
163
  ```tsx
85
- // app/layout.tsx (Server Component — no "use client")
86
- import { Footer } from '@/components/layout/Footer';
164
+ // app/layout.tsx (Server Component)
165
+ import { SiteChrome } from '@/components/layout/SiteChrome';
166
+ import layout from 'weg-shared-layout/dummy-data.json';
87
167
 
88
168
  export default function RootLayout({ children }: { children: React.ReactNode }) {
89
169
  return (
90
170
  <html lang="en">
91
171
  <body>
92
- {children}
93
- <Footer />
172
+ <SiteChrome layout={layout}>{children}</SiteChrome>
94
173
  </body>
95
174
  </html>
96
175
  );
97
176
  }
98
177
  ```
99
178
 
100
- Fetch layout on the server and pass it into `<Footer layout={...} />` when you wire production data (see below).
101
-
102
- ## 4. Production: fetch on the server, stringify for the client
179
+ ## 4. Production: fetch layout on the server
103
180
 
104
181
  ```tsx
105
182
  // app/layout.tsx
106
- import { Footer } from '@/components/layout/Footer';
183
+ import { SiteChrome } from '@/components/layout/SiteChrome';
107
184
 
108
185
  const LAYOUT_URL = 'https://weg-payload-test.vercel.app/api/layout';
109
186
 
@@ -119,62 +196,50 @@ export default async function RootLayout({ children }: { children: React.ReactNo
119
196
  return (
120
197
  <html lang="en">
121
198
  <body>
122
- {children}
123
- <Footer layout={layout} />
199
+ <SiteChrome layout={layout}>{children}</SiteChrome>
124
200
  </body>
125
201
  </html>
126
202
  );
127
203
  }
128
204
  ```
129
205
 
130
- ```tsx
131
- // src/components/layout/Footer.tsx
132
- 'use client';
206
+ Pass the plain object into the Client Component; **stringify inside** the header/footer wrappers.
133
207
 
134
- import { defineCustomElements } from 'weg-shared-layout/loader';
135
- import 'weg-shared-layout/weg-footer';
208
+ Wire **`signedIn`** from your auth provider (e.g. session from a client context populated after mount, or a client-only wrapper around `<Header />`).
136
209
 
137
- defineCustomElements();
138
-
139
- type LayoutData = {
140
- footer?: {
141
- social?: { platform: string; href: string }[];
142
- columns?: { links: { label: string; href: string }[] }[];
143
- credits?: string;
144
- copyright?: string;
145
- };
146
- };
210
+ ## Header auth reference
147
211
 
148
- export function Footer({ layout }: { layout: LayoutData }) {
149
- return (
150
- <weg-footer layout={JSON.stringify(layout)} suppressHydrationWarning />
151
- );
152
- }
153
- ```
212
+ | API | Usage |
213
+ | --- | --- |
214
+ | `layout.header.signIn` | `{ label, href }` shown when `signed-in` is false |
215
+ | `layout.header.signOut` | `{ label, href? }` — shown when `signed-in` is true |
216
+ | `signed-in` prop | Boolean session flag from host app |
217
+ | `wegAuthClick` event | `event.detail.action`: `'sign-in'` \| `'sign-out'` |
218
+ | `event.preventDefault()` | Skip default link navigation / redirect |
154
219
 
155
- **Rule:** whatever crosses the Server → Client boundary must be **serializable**. Pass the plain object into the Client Component, then **`JSON.stringify` inside the client** before setting it on `<weg-footer>`.
220
+ Logo is bundled in the component not in `layout`.
156
221
 
157
222
  ## Passing `layout` — quick reference
158
223
 
159
- | Approach | Server Component | Client Component | Works in Next? |
160
- | --- | --- | --- | --- |
161
- | `layout={object}` on `<weg-footer>` | No (don’t use CE directly) | Tried often | **No** |
162
- | `layout={JSON.stringify(obj)}` on `<weg-footer>` | No | Yes | **Yes** (recommended) |
163
- | Pass `layout` object to `<Footer />`, stringify inside | Fetch/await JSON | `JSON.stringify` in JSX | **Yes** (recommended for CMS) |
164
- | `ref` + `el.layout = obj` in `useEffect` | No | Yes | Yes (escape hatch) |
165
- | `prop:layout={obj}` | No | Unreliable | **Avoid** |
224
+ | Approach | Works in Next? |
225
+ | --- | --- |
226
+ | `layout={object}` on `<weg-header>` / `<weg-footer>` | **No** |
227
+ | `layout={JSON.stringify(obj)}` in client wrapper | **Yes** (recommended) |
228
+ | Pass object to `<SiteChrome layout={...} />`, stringify in child | **Yes** |
229
+ | `ref` + `el.layout = obj` in `useEffect` | Yes (escape hatch) |
166
230
 
167
231
  ## TypeScript
168
232
 
169
- Use **module augmentation** (not triple-slash references in every file). Example `src/types/weg-shared-layout-jsx.d.ts`:
170
-
171
233
  ```ts
234
+ // src/types/weg-shared-layout-jsx.d.ts
172
235
  import type { JSX as WegSharedLayoutJSX } from 'weg-shared-layout';
173
236
  import type { DetailedHTMLProps, HTMLAttributes } from 'react';
174
237
 
175
238
  declare module 'react' {
176
239
  namespace JSX {
177
240
  interface IntrinsicElements extends WegSharedLayoutJSX.IntrinsicElements {
241
+ 'weg-header': WegSharedLayoutJSX.IntrinsicElements['weg-header'] &
242
+ DetailedHTMLProps<HTMLAttributes<HTMLWegHeaderElement>, HTMLWegHeaderElement>;
178
243
  'weg-footer': WegSharedLayoutJSX.IntrinsicElements['weg-footer'] &
179
244
  DetailedHTMLProps<HTMLAttributes<HTMLWegFooterElement>, HTMLWegFooterElement>;
180
245
  }
@@ -182,62 +247,31 @@ declare module 'react' {
182
247
  }
183
248
  ```
184
249
 
185
- Enable `resolveJsonModule` if you import `weg-shared-layout/dummy-data.json` in the client footer.
186
-
187
250
  ## Troubleshooting
188
251
 
189
252
  | Symptom | Likely cause | Fix |
190
253
  | --- | --- | --- |
191
- | `document is not defined` | `defineCustomElements()` in a Server Component | Move registration to `"use client"` `Footer.tsx` only. |
192
- | Empty `<weg-footer>` in Elements panel | SSR placeholder before hydration | Expected until client JS runs; confirm client bundle loads and `defineCustomElements()` ran. |
193
- | Empty footer after hydration | `layout={object}` or missing stringify | Use `layout={JSON.stringify(layout)}` in the client component. |
194
- | Hydration warning on `<weg-footer>` | Server HTML client DOM | Add `suppressHydrationWarning`; ensure `layout` JSON is identical server vs client props. |
195
- | Build error importing package | Package not transpiled | Add `transpilePackages: ['weg-shared-layout']`. |
196
- | `'weg-footer' is not a known element` (TS) | Missing JSX types | Add module augmentation file above. |
197
- | Footer never upgrades | Loader/tag not imported on client | `import 'weg-shared-layout/weg-footer'` + `defineCustomElements()` in client file. |
198
-
199
- ### DevTools tips
200
-
201
- 1. **Elements:** Before hydration, `<weg-footer>` is often empty. After hydration, expand the shadow root — links should appear inside `#shadow-root`.
202
- 2. **Console:** Log `document.querySelector('weg-footer')?.layout` in the browser — should be a **string** (JSON) or object, not `undefined`.
203
- 3. **Network:** Confirm your layout API returns the same shape as [`dummy-data.json`](../src/assets/dummy-data.json).
204
- 4. **React DevTools:** `Footer` should show under a Client Component boundary; parent `layout.tsx` stays a Server Component.
205
-
206
- ### Ref fallback (if stringify still fails)
207
-
208
- ```tsx
209
- 'use client';
210
-
211
- import { useEffect, useRef } from 'react';
212
- import { defineCustomElements } from 'weg-shared-layout/loader';
213
- import 'weg-shared-layout/weg-footer';
214
-
215
- defineCustomElements();
216
-
217
- export function Footer({ layout }: { layout: unknown }) {
218
- const ref = useRef<HTMLWegFooterElement>(null);
219
-
220
- useEffect(() => {
221
- if (ref.current) ref.current.layout = layout as object;
222
- }, [layout]);
223
-
224
- return <weg-footer ref={ref} suppressHydrationWarning />;
225
- }
226
- ```
254
+ | `document is not defined` | Loader in Server Component | Keep registration in `"use client"` files only. |
255
+ | Empty after hydration | Object passed to CE without stringify | `JSON.stringify` in client wrapper. |
256
+ | Logo missing | Stale package | Logo is inlined in `logo-data.ts`; rebuild / upgrade. |
257
+ | Auth not updating | `signed-in` only on server | Manage session in client state / context. |
258
+ | Hydration warning | Shadow DOM mismatch | `suppressHydrationWarning` on both tags. |
259
+ | Build error | Package not transpiled | `transpilePackages: ['weg-shared-layout']`. |
227
260
 
228
261
  ## Integration checklist
229
262
 
230
263
  - [ ] `weg-shared-layout` installed
231
- - [ ] `transpilePackages: ['weg-shared-layout']` in `next.config`
232
- - [ ] `Footer.tsx` with `'use client'`, loader, tag import, `JSON.stringify`, `suppressHydrationWarning`
233
- - [ ] Root `app/layout.tsx` imports `<Footer />` (no `defineCustomElements` on server)
234
- - [ ] Layout data fetched server-side (or static import) and passed as serializable props
235
- - [ ] TypeScript module augmentation for `'weg-footer'`
236
- - [ ] Verified in browser after hydration (not only view-source HTML)
264
+ - [ ] `transpilePackages` in `next.config`
265
+ - [ ] Client `Header.tsx` / `Footer.tsx` with loader, tag imports, `JSON.stringify`, `suppressHydrationWarning`
266
+ - [ ] Auth: `signed-in` + `wegAuthClick` handler on header
267
+ - [ ] Server `layout.tsx` imports client chrome only (no `defineCustomElements` on server)
268
+ - [ ] Layout fetched or imported server-side, passed as serializable props
269
+ - [ ] TypeScript augmentation for `'weg-header'` and `'weg-footer'`
270
+ - [ ] Verified in browser after hydration
237
271
 
238
272
  ## See also
239
273
 
240
- - **[React SPA](./react.md)** — object `layout={...}` on React 19 without SSR
274
+ - **[React SPA](./react.md)**
241
275
  - **[Angular](./angular.md)**
242
276
  - **[Plain HTML / vanilla JS](./vanilla.md)**
243
277
  - **[Package readme](../readme.md)**