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.
- package/dist/cjs/{index-CmiaQ_Dj.js → index-CC8e1qRm.js} +112 -0
- package/dist/cjs/loader.cjs.js +2 -2
- package/dist/cjs/my-component.cjs.entry.js +1 -1
- package/dist/cjs/weg-footer_2.cjs.entry.js +438 -0
- package/dist/cjs/weg-shared-layout.cjs.js +2 -2
- package/dist/collection/collection-manifest.json +2 -1
- package/dist/collection/components/weg-footer/weg-footer.css +1 -24
- package/dist/collection/components/weg-footer/weg-footer.js +5 -40
- package/dist/collection/components/weg-header/logo-data.js +2 -0
- package/dist/collection/components/weg-header/weg-header.cmp.test.js +77 -0
- package/dist/collection/components/weg-header/weg-header.css +327 -0
- package/dist/collection/components/weg-header/weg-header.js +344 -0
- package/dist/collection/styles/shared.css +49 -0
- package/dist/collection/utils/layout.js +33 -0
- package/dist/collection/utils/layout.unit.test.js +36 -0
- package/dist/components/index.js +1 -1
- package/dist/components/my-component.js +1 -1
- package/dist/components/p-CtYuWNO6.js +1 -0
- package/dist/components/p-DbIEJ3IT.js +1 -0
- package/dist/components/weg-footer.js +1 -1
- package/dist/components/weg-header.d.ts +11 -0
- package/dist/components/weg-header.js +1 -0
- package/dist/esm/{index-QiJxC4Ow.js → index-D8pmhPiH.js} +111 -1
- package/dist/esm/loader.js +3 -3
- package/dist/esm/my-component.entry.js +1 -1
- package/dist/esm/weg-footer_2.entry.js +435 -0
- package/dist/esm/weg-shared-layout.js +3 -3
- package/dist/types/components/weg-header/logo-data.d.ts +2 -0
- package/dist/types/components/weg-header/weg-header.cmp.test.d.ts +1 -0
- package/dist/types/components/weg-header/weg-header.d.ts +56 -0
- package/dist/types/components.d.ts +56 -2
- package/dist/types/types/layout-data.d.ts +21 -4
- package/dist/types/utils/layout.d.ts +4 -0
- package/dist/types/utils/layout.unit.test.d.ts +1 -0
- package/dist/weg-shared-layout/p-D8pmhPiH.js +2 -0
- package/dist/weg-shared-layout/{p-d1addb13.entry.js → p-d61033bd.entry.js} +1 -1
- package/dist/weg-shared-layout/p-eaf953a4.entry.js +1 -0
- package/dist/weg-shared-layout/weg-shared-layout.esm.js +1 -1
- package/docs/angular.md +66 -18
- package/docs/nextjs.md +145 -111
- package/docs/react.md +123 -59
- package/docs/vanilla.md +99 -3
- package/package.json +5 -1
- package/readme.md +31 -3
- package/src/assets/dummy-data.json +41 -1
- package/dist/cjs/weg-footer.cjs.entry.js +0 -189
- package/dist/components/p-BTQYW5OR.js +0 -1
- package/dist/esm/weg-footer.entry.js +0 -187
- package/dist/weg-shared-layout/p-0c28f34f.entry.js +0 -1
- 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
|
|
10
|
-
| **Server Components** | N/A | Default in `app/` — cannot call `defineCustomElements()`
|
|
11
|
-
| **SSR output** | N/A |
|
|
12
|
-
| **`layout={object}
|
|
13
|
-
| **
|
|
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
|
-
|
|
|
23
|
-
| **`
|
|
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
|
-
|
|
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
|
-
|
|
63
|
+
defineCustomElements();
|
|
50
64
|
|
|
51
|
-
|
|
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
|
-
|
|
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
|
|
151
|
+
### Why `JSON.stringify` and `suppressHydrationWarning`
|
|
71
152
|
|
|
72
153
|
| Line | Purpose |
|
|
73
154
|
| --- | --- |
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
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}`
|
|
159
|
+
Do **not** pass `layout={layoutObject}` directly on the custom elements in Next.
|
|
81
160
|
|
|
82
|
-
## 3. Server layout
|
|
161
|
+
## 3. Server layout
|
|
83
162
|
|
|
84
163
|
```tsx
|
|
85
|
-
// app/layout.tsx (Server Component
|
|
86
|
-
import {
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
220
|
+
Logo is bundled in the component — not in `layout`.
|
|
156
221
|
|
|
157
222
|
## Passing `layout` — quick reference
|
|
158
223
|
|
|
159
|
-
| Approach |
|
|
160
|
-
| --- | --- |
|
|
161
|
-
| `layout={object}` on `<weg-
|
|
162
|
-
| `layout={JSON.stringify(obj)}`
|
|
163
|
-
| Pass
|
|
164
|
-
| `ref` + `el.layout = obj` in `useEffect` |
|
|
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` |
|
|
192
|
-
| Empty
|
|
193
|
-
|
|
|
194
|
-
|
|
|
195
|
-
|
|
|
196
|
-
|
|
|
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
|
|
232
|
-
- [ ] `
|
|
233
|
-
- [ ]
|
|
234
|
-
- [ ]
|
|
235
|
-
- [ ]
|
|
236
|
-
- [ ]
|
|
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)**
|
|
274
|
+
- **[React SPA](./react.md)**
|
|
241
275
|
- **[Angular](./angular.md)**
|
|
242
276
|
- **[Plain HTML / vanilla JS](./vanilla.md)**
|
|
243
277
|
- **[Package readme](../readme.md)**
|