@startnext/chrome 0.3.4 → 0.4.0
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/README.md +44 -400
- package/dist/components/base/BaseComponent.d.ts +0 -2
- package/dist/components/base/BaseComponent.d.ts.map +1 -1
- package/dist/components/footer/StartnextFooter.d.ts +2 -3
- package/dist/components/footer/StartnextFooter.d.ts.map +1 -1
- package/dist/components/header/StartnextHeader.d.ts +3 -3
- package/dist/components/header/StartnextHeader.d.ts.map +1 -1
- package/dist/components/header/header.css.d.ts +1 -1
- package/dist/components/header/header.css.d.ts.map +1 -1
- package/dist/components/header/header.template.d.ts +6 -1
- package/dist/components/header/header.template.d.ts.map +1 -1
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.umd.js +2 -2
- package/dist/render.js +1 -1
- package/package.json +9 -18
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Startnext Header/Footer Web Components
|
|
4
4
|
|
|
5
|
-
Wiederverwendbare Header- und Footer-Components
|
|
5
|
+
Wiederverwendbare Header- und Footer-Components für alle Startnext Microservices.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -14,41 +14,24 @@ pnpm add @startnext/chrome
|
|
|
14
14
|
|
|
15
15
|
## Quick Start
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
1. **Client-Side Rendering** — Web Component fetcht Daten selbst (einfach, kein Server noetig)
|
|
20
|
-
2. **Server-Side Rendering (SSR)** — Server holt fertig gerendertes HTML von der API (sofort sichtbar ohne JS, bessere Performance + SEO)
|
|
21
|
-
|
|
22
|
-
### Client-Side: Via CDN (PHP, Vanilla HTML)
|
|
23
|
-
```html
|
|
24
|
-
<script type="module" src="https://unpkg.com/@startnext/chrome@latest/dist/index.js"></script>
|
|
25
|
-
|
|
26
|
-
<startnext-header api-url="https://scs-api.vercel.app"></startnext-header>
|
|
27
|
-
<startnext-footer api-url="https://scs-api.vercel.app"></startnext-footer>
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### Client-Side: Modern JavaScript (React, Vue, etc.)
|
|
17
|
+
### Modern JavaScript (React, Vue, etc.)
|
|
31
18
|
```javascript
|
|
32
19
|
import '@startnext/chrome';
|
|
33
20
|
```
|
|
34
21
|
|
|
35
22
|
```html
|
|
36
|
-
<startnext-header
|
|
37
|
-
<startnext-footer
|
|
23
|
+
<startnext-header></startnext-header>
|
|
24
|
+
<startnext-footer></startnext-footer>
|
|
38
25
|
```
|
|
39
26
|
|
|
40
|
-
###
|
|
41
|
-
|
|
42
|
-
|
|
27
|
+
### Via CDN (PHP, Vanilla HTML)
|
|
28
|
+
```html
|
|
29
|
+
<script type="module" src="https://unpkg.com/@startnext/chrome@latest/dist/index.js"></script>
|
|
43
30
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
curl "https://scs-api.vercel.app/api/header/render?lang=de&large-animation"
|
|
47
|
-
curl "https://scs-api.vercel.app/api/footer/render?lang=de"
|
|
31
|
+
<startnext-header></startnext-header>
|
|
32
|
+
<startnext-footer></startnext-footer>
|
|
48
33
|
```
|
|
49
34
|
|
|
50
|
-
Siehe Framework-spezifische Beispiele: [React / Next.js](#react--nextjs-app-router), [Vue / Nuxt](#vue--nuxt-3), [PHP / Vanilla](#php--vanilla-html)
|
|
51
|
-
|
|
52
35
|
## API
|
|
53
36
|
|
|
54
37
|
### `<startnext-header>`
|
|
@@ -57,47 +40,43 @@ Siehe Framework-spezifische Beispiele: [React / Next.js](#react--nextjs-app-rout
|
|
|
57
40
|
|
|
58
41
|
| Attribute | Type | Default | Description |
|
|
59
42
|
|---|---|---|---|
|
|
60
|
-
| `lang` | `string` | `'de'` | Sprache (`'de'`, `'en'`) |
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
43
|
+
| `lang` | `string` | `'de'` | Aktive Sprache (beliebiger 2-Buchstaben-Code, z. B. `'de'`, `'en'`, `'fr'`) |
|
|
44
|
+
| `languages` | `string` (JSON) | - | JSON-Array von Sprachcodes, auf die der Language-Switcher eingeschränkt wird, z. B. `'["de","en"]'`. Ohne Attribut: alle vom API gelieferten Sprachen. Der Switcher wird automatisch ausgeblendet, wenn nur 1 Sprache verfügbar ist |
|
|
45
|
+
| `light` | `boolean` | `false` | Heller Header (weiße Schrift/Icons über dunklem Hero, wechselt automatisch zu dunkel beim Scrollen). Standard ohne Attribut: dunkler Header |
|
|
46
|
+
| `large-animation` | `boolean` | `false` | Große Lottie-Logo-Animation mit Claim und horizontaler Navigation |
|
|
63
47
|
| `authenticated` | `boolean` | `false` | Zeigt User-Avatar statt "Anmelden" Button |
|
|
64
48
|
| `user-name` | `string` | - | Name des eingeloggten Users |
|
|
65
49
|
| `user-avatar` | `string` | - | Avatar-URL des Users |
|
|
66
50
|
| `color-mode` | `'light' \| 'dark'` | auto | Farbmodus. Ohne Attribut: Header scannt `<html>` nach Klasse `dark`/`light`. Ohne Klasse: Default `dark` (dunkle Seite, heller Header). Toggle-Button wechselt `<html>`-Klasse und Header-Darstellung |
|
|
67
51
|
| `hide-color-mode` | `boolean` | `false` | Versteckt den Color-Mode Toggle-Button |
|
|
68
|
-
| `hide-lang` | `boolean` | `false` | Versteckt den Language-Switcher |
|
|
69
|
-
| `hide-login` | `boolean` | `false` | Versteckt den Login/Avatar-Button |
|
|
70
|
-
| `show-back-link` | `boolean` | `false` | Zeigt einen "Zurueck zu Startnext"-Link (Text kommt von der API) |
|
|
71
|
-
| `back-url` | `string` | - | Ueberschreibt die Back-Link-URL |
|
|
72
|
-
| `back-label` | `string` | - | Ueberschreibt den Back-Link-Text |
|
|
73
|
-
| `api-url` | `string` | - | API-Endpoint-URL fuer Live-Daten aus Notion (nicht noetig bei SSR) |
|
|
52
|
+
| `hide-lang` | `boolean` | `false` | Versteckt den Language-Switcher komplett |
|
|
74
53
|
|
|
75
54
|
**Events:**
|
|
76
55
|
|
|
77
56
|
| Event | Detail | Description |
|
|
78
57
|
|---|---|---|
|
|
79
|
-
| `burger-menu-toggle` | `{ open: boolean }` | Burger-
|
|
80
|
-
| `user-menu-toggle` | `{ open: boolean }` | User-
|
|
58
|
+
| `burger-menu-toggle` | `{ open: boolean }` | Burger-Menü geöffnet/geschlossen |
|
|
59
|
+
| `user-menu-toggle` | `{ open: boolean }` | User-Menü geöffnet/geschlossen |
|
|
81
60
|
| `navigation-click` | `{ item: NavigationItem }` | Navigation-Link geklickt |
|
|
82
61
|
| `cta-click` | `{ url: string }` | "Projekt starten" Button geklickt |
|
|
83
62
|
| `logout` | `{}` | Abmelden geklickt |
|
|
84
63
|
| `language-change` | `{ language: string }` | Sprache gewechselt |
|
|
85
|
-
| `scroll-state-change` | `{ scrolled: boolean, slideUp: boolean }` | Scroll-Zustand
|
|
64
|
+
| `scroll-state-change` | `{ scrolled: boolean, slideUp: boolean }` | Scroll-Zustand geändert |
|
|
86
65
|
| `color-mode-change` | `{ mode: 'light' \| 'dark' }` | Farbmodus gewechselt (per Toggle-Button) |
|
|
87
66
|
|
|
88
67
|
**Usage Examples:**
|
|
89
68
|
|
|
90
69
|
```html
|
|
91
|
-
<!-- Default: dunkler Header (dunkle Schrift
|
|
70
|
+
<!-- Default: dunkler Header (dunkle Schrift für helle Seiten) -->
|
|
92
71
|
<startnext-header></startnext-header>
|
|
93
72
|
|
|
94
|
-
<!-- Heller Header (
|
|
73
|
+
<!-- Heller Header (weiße Schrift über dunklem Hero) -->
|
|
95
74
|
<startnext-header light></startnext-header>
|
|
96
75
|
|
|
97
|
-
<!-- Heller Header +
|
|
76
|
+
<!-- Heller Header + große Animation (z.B. Startseite) -->
|
|
98
77
|
<startnext-header light large-animation></startnext-header>
|
|
99
78
|
|
|
100
|
-
<!-- Heller Header +
|
|
79
|
+
<!-- Heller Header + groß + eingeloggt -->
|
|
101
80
|
<startnext-header
|
|
102
81
|
light
|
|
103
82
|
large-animation
|
|
@@ -116,17 +95,6 @@ Siehe Framework-spezifische Beispiele: [React / Next.js](#react--nextjs-app-rout
|
|
|
116
95
|
</html>
|
|
117
96
|
```
|
|
118
97
|
|
|
119
|
-
```html
|
|
120
|
-
<!-- Embedded / Microservice-Modus: Login ausblenden, Back-Link anzeigen -->
|
|
121
|
-
<startnext-header
|
|
122
|
-
hide-login
|
|
123
|
-
show-back-link
|
|
124
|
-
back-url="https://www.startnext.com"
|
|
125
|
-
back-label="Zurueck zu Startnext"
|
|
126
|
-
api-url="https://scs-api.vercel.app"
|
|
127
|
-
></startnext-header>
|
|
128
|
-
```
|
|
129
|
-
|
|
130
98
|
```javascript
|
|
131
99
|
const header = document.querySelector('startnext-header');
|
|
132
100
|
|
|
@@ -136,9 +104,8 @@ header.addEventListener('navigation-click', (e) => {
|
|
|
136
104
|
});
|
|
137
105
|
|
|
138
106
|
header.addEventListener('language-change', (e) => {
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
// document.querySelector('startnext-footer').setAttribute('lang', e.detail.language);
|
|
107
|
+
// Sprache auch im Footer synchronisieren
|
|
108
|
+
document.querySelector('startnext-footer').setAttribute('lang', e.detail.language);
|
|
142
109
|
});
|
|
143
110
|
|
|
144
111
|
header.addEventListener('logout', () => {
|
|
@@ -157,9 +124,9 @@ header.addEventListener('color-mode-change', (e) => {
|
|
|
157
124
|
|
|
158
125
|
| Attribute | Type | Default | Description |
|
|
159
126
|
|---|---|---|---|
|
|
160
|
-
| `lang` | `string` | `'de'` | Sprache (
|
|
161
|
-
| `api-url` | `string` | - |
|
|
162
|
-
| `lang-sync` | `boolean` | `true` | Synchronisiert
|
|
127
|
+
| `lang` | `string` | `'de'` | Aktive Sprache (beliebiger 2-Buchstaben-Code) |
|
|
128
|
+
| `api-url` | `string` | - | URL des SCS API. Ohne Attribut: statische Default-Daten |
|
|
129
|
+
| `lang-sync` | `boolean` | `true` | Synchronisiert `lang` automatisch beim `language-change`-Event des Headers |
|
|
163
130
|
|
|
164
131
|
**Events:**
|
|
165
132
|
|
|
@@ -186,7 +153,7 @@ startnext-footer {
|
|
|
186
153
|
|
|
187
154
|
## Fonts
|
|
188
155
|
|
|
189
|
-
|
|
156
|
+
Standardmäßig nutzen die Components System-Fonts als Fallback. Für eigene Schriftarten `@font-face` im eigenen CSS deklarieren und `--font-family` setzen:
|
|
190
157
|
|
|
191
158
|
```css
|
|
192
159
|
@font-face {
|
|
@@ -202,329 +169,11 @@ startnext-header, startnext-footer {
|
|
|
202
169
|
}
|
|
203
170
|
```
|
|
204
171
|
|
|
205
|
-
## Server-Side Rendering (SSR)
|
|
206
|
-
|
|
207
|
-
Die API liefert fertig gerendertes HTML mit **Declarative Shadow DOM** (`<template shadowrootmode="open">`) und eingebetteten **API-Daten als JSON** (`<script data-ssr-props>`). Der Browser zeigt den Content sofort an — ohne JavaScript. Die Web Component JS hydriert anschliessend (Events anbinden, kein Re-Render noetig).
|
|
208
|
-
|
|
209
|
-
### Wie es funktioniert
|
|
210
|
-
|
|
211
|
-
1. Server fetcht SSR-HTML von `/api/header/render` bzw. `/api/footer/render`
|
|
212
|
-
2. HTML wird direkt in die Seite eingebunden
|
|
213
|
-
3. Browser parst DSD → Shadow DOM existiert sofort, Content ist sichtbar
|
|
214
|
-
4. Web Component JS laedt → erkennt bestehenden `shadowRoot`, setzt `isHydrating = true`
|
|
215
|
-
5. `render()` ueberspringt `innerHTML`, ruft nur `attachEvents()` auf
|
|
216
|
-
6. `parseSsrProps()` liest eingebettetes JSON → populiert API-Cache mit echten Daten
|
|
217
|
-
7. Attribut-Aenderungen (z.B. `color-mode`, `large-animation`) funktionieren sofort — kein Client-Fetch noetig
|
|
218
|
-
|
|
219
|
-
```html
|
|
220
|
-
<!-- Server liefert fertiges HTML (vereinfacht) -->
|
|
221
|
-
<startnext-header lang="de" light large-animation>
|
|
222
|
-
<template shadowrootmode="open">
|
|
223
|
-
<style>/* headerCSS */</style>
|
|
224
|
-
<div><!-- Shadow DOM HTML --></div>
|
|
225
|
-
</template>
|
|
226
|
-
<script type="application/json" data-ssr-props>{"mainNavigation":[...],...}</script>
|
|
227
|
-
</startnext-header>
|
|
228
|
-
|
|
229
|
-
<!-- JS hydriert automatisch -->
|
|
230
|
-
<script type="module" src="@startnext/chrome"></script>
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
### Render-Endpoints (Query-Parameter)
|
|
234
|
-
|
|
235
|
-
**Header:** `GET /api/header/render`
|
|
236
|
-
|
|
237
|
-
| Parameter | Typ | Beschreibung |
|
|
238
|
-
|-----------|-----|-------------|
|
|
239
|
-
| `lang` | `de` \| `en` | Sprache |
|
|
240
|
-
| `light` | flag | Heller Header |
|
|
241
|
-
| `large-animation` | flag | Grosse Logo-Animation |
|
|
242
|
-
| `authenticated` | flag | Eingeloggt |
|
|
243
|
-
| `user-name` | string | User-Name |
|
|
244
|
-
| `user-avatar` | string | Avatar-URL |
|
|
245
|
-
| `hide-color-mode` | flag | Color-Mode Toggle verstecken |
|
|
246
|
-
| `hide-lang` | flag | Language-Switcher verstecken |
|
|
247
|
-
| `hide-login` | flag | Login-Button verstecken |
|
|
248
|
-
| `show-back-link` | flag | Back-Link anzeigen |
|
|
249
|
-
| `back-url` | string | Back-Link URL |
|
|
250
|
-
| `back-label` | string | Back-Link Text |
|
|
251
|
-
|
|
252
|
-
**Footer:** `GET /api/footer/render`
|
|
253
|
-
|
|
254
|
-
| Parameter | Typ | Beschreibung |
|
|
255
|
-
|-----------|-----|-------------|
|
|
256
|
-
| `lang` | `de` \| `en` | Sprache |
|
|
257
|
-
|
|
258
|
-
Beispiel: `GET /api/header/render?lang=de&large-animation&hide-login&show-back-link`
|
|
259
|
-
|
|
260
|
-
### `api-url` bei SSR nicht noetig
|
|
261
|
-
|
|
262
|
-
Bei SSR werden die API-Daten als JSON im HTML eingebettet (`<script data-ssr-props>`). Der Component liest das beim Hydrating und hat sofort echte Daten im Cache. `api-url` ist daher **nicht noetig** — der Component rendert und reagiert auf Attribut-Aenderungen ohne zusaetzlichen Fetch.
|
|
263
|
-
|
|
264
|
-
`api-url` ist nur fuer **Client-Side-Only** Rendering relevant, wenn kein SSR-HTML verfuegbar ist.
|
|
265
|
-
|
|
266
|
-
---
|
|
267
|
-
|
|
268
|
-
## Framework-Integration (SSR)
|
|
269
|
-
|
|
270
|
-
### React / Next.js (App Router)
|
|
271
|
-
|
|
272
|
-
React's `dangerouslySetInnerHTML` nutzt `innerHTML`, das DSD nicht parsed. Nach der Hydration wird `setHTMLUnsafe()` als Fallback aufgerufen.
|
|
273
|
-
|
|
274
|
-
**Layout (Server Component):**
|
|
275
|
-
|
|
276
|
-
```tsx
|
|
277
|
-
// app/[locale]/layout.tsx
|
|
278
|
-
async function fetchChromeHtml(path: string): Promise<string> {
|
|
279
|
-
try {
|
|
280
|
-
const res = await fetch(`${process.env.NEXT_PUBLIC_SCS_API_URL}${path}`, {
|
|
281
|
-
next: { revalidate: 60 },
|
|
282
|
-
});
|
|
283
|
-
if (!res.ok) return "";
|
|
284
|
-
return await res.text();
|
|
285
|
-
} catch {
|
|
286
|
-
return "";
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
export default async function Layout({ children, params }) {
|
|
291
|
-
const { locale } = await params;
|
|
292
|
-
const [headerHtml, footerHtml] = await Promise.all([
|
|
293
|
-
fetchChromeHtml(`/api/header/render?lang=${locale}&large-animation`),
|
|
294
|
-
fetchChromeHtml(`/api/footer/render?lang=${locale}`),
|
|
295
|
-
]);
|
|
296
|
-
|
|
297
|
-
return (
|
|
298
|
-
<html lang={locale}>
|
|
299
|
-
<body>
|
|
300
|
-
<ChromeHeader ssrHtml={headerHtml} />
|
|
301
|
-
<main>{children}</main>
|
|
302
|
-
<ChromeFooter ssrHtml={footerHtml} />
|
|
303
|
-
</body>
|
|
304
|
-
</html>
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
**Header (Client Component):**
|
|
310
|
-
|
|
311
|
-
```tsx
|
|
312
|
-
// components/ChromeHeader.tsx
|
|
313
|
-
"use client";
|
|
314
|
-
import { useEffect, useRef, useState } from "react";
|
|
315
|
-
|
|
316
|
-
export function ChromeHeader({ ssrHtml }: { ssrHtml?: string }) {
|
|
317
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
318
|
-
const [mounted, setMounted] = useState(false);
|
|
319
|
-
|
|
320
|
-
useEffect(() => {
|
|
321
|
-
const container = containerRef.current;
|
|
322
|
-
if (container && ssrHtml) {
|
|
323
|
-
const header = container.querySelector("startnext-header");
|
|
324
|
-
if (!header?.shadowRoot && "setHTMLUnsafe" in Element.prototype) {
|
|
325
|
-
container.setHTMLUnsafe(ssrHtml);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
import("@startnext/chrome");
|
|
329
|
-
setMounted(true);
|
|
330
|
-
}, [ssrHtml]);
|
|
331
|
-
|
|
332
|
-
// Attribut-Sync nach Mount (z.B. color-mode, large-animation)
|
|
333
|
-
useEffect(() => {
|
|
334
|
-
const el = containerRef.current?.querySelector("startnext-header");
|
|
335
|
-
if (!el || !mounted) return;
|
|
336
|
-
// Beispiel: el.setAttribute("color-mode", resolvedTheme);
|
|
337
|
-
}, [mounted]);
|
|
338
|
-
|
|
339
|
-
if (ssrHtml) {
|
|
340
|
-
return (
|
|
341
|
-
<div ref={containerRef} style={{ display: "contents" }}
|
|
342
|
-
suppressHydrationWarning
|
|
343
|
-
dangerouslySetInnerHTML={{ __html: ssrHtml }}
|
|
344
|
-
/>
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// Fallback ohne SSR
|
|
349
|
-
return <startnext-header api-url={process.env.NEXT_PUBLIC_SCS_API_URL} />;
|
|
350
|
-
}
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
**Footer (Client Component):**
|
|
354
|
-
|
|
355
|
-
```tsx
|
|
356
|
-
// components/ChromeFooter.tsx
|
|
357
|
-
"use client";
|
|
358
|
-
import { useEffect, useRef } from "react";
|
|
359
|
-
|
|
360
|
-
export function ChromeFooter({ ssrHtml }: { ssrHtml?: string }) {
|
|
361
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
362
|
-
|
|
363
|
-
useEffect(() => {
|
|
364
|
-
const container = containerRef.current;
|
|
365
|
-
if (container && ssrHtml) {
|
|
366
|
-
const footer = container.querySelector("startnext-footer");
|
|
367
|
-
if (!footer?.shadowRoot && "setHTMLUnsafe" in Element.prototype) {
|
|
368
|
-
container.setHTMLUnsafe(ssrHtml);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
import("@startnext/chrome");
|
|
372
|
-
}, [ssrHtml]);
|
|
373
|
-
|
|
374
|
-
if (ssrHtml) {
|
|
375
|
-
return (
|
|
376
|
-
<div ref={containerRef} style={{ display: "contents" }}
|
|
377
|
-
suppressHydrationWarning
|
|
378
|
-
dangerouslySetInnerHTML={{ __html: ssrHtml }}
|
|
379
|
-
/>
|
|
380
|
-
);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
return <startnext-footer api-url={process.env.NEXT_PUBLIC_SCS_API_URL} />;
|
|
384
|
-
}
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
### Vue / Nuxt 3
|
|
388
|
-
|
|
389
|
-
Vue parsed DSD korrekt via `v-html` — kein `setHTMLUnsafe()` Workaround noetig.
|
|
390
|
-
|
|
391
|
-
**Server Plugin (SSR-HTML fetchen):**
|
|
392
|
-
|
|
393
|
-
```ts
|
|
394
|
-
// server/plugins/chrome.ts
|
|
395
|
-
export default defineEventHandler(async (event) => {
|
|
396
|
-
// Wird automatisch von useAsyncData / useFetch aufgerufen
|
|
397
|
-
});
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
**Layout:**
|
|
401
|
-
|
|
402
|
-
```vue
|
|
403
|
-
<!-- layouts/default.vue -->
|
|
404
|
-
<script setup lang="ts">
|
|
405
|
-
const { locale } = useI18n();
|
|
406
|
-
const config = useRuntimeConfig();
|
|
407
|
-
|
|
408
|
-
const { data: headerHtml } = await useFetch(
|
|
409
|
-
() => `${config.public.scsApiUrl}/api/header/render?lang=${locale.value}&large-animation`,
|
|
410
|
-
{ key: `chrome-header-${locale.value}` }
|
|
411
|
-
);
|
|
412
|
-
|
|
413
|
-
const { data: footerHtml } = await useFetch(
|
|
414
|
-
() => `${config.public.scsApiUrl}/api/footer/render?lang=${locale.value}`,
|
|
415
|
-
{ key: `chrome-footer-${locale.value}` }
|
|
416
|
-
);
|
|
417
|
-
|
|
418
|
-
onMounted(() => {
|
|
419
|
-
import('@startnext/chrome');
|
|
420
|
-
});
|
|
421
|
-
</script>
|
|
422
|
-
|
|
423
|
-
<template>
|
|
424
|
-
<div v-if="headerHtml" v-html="headerHtml" style="display: contents" />
|
|
425
|
-
<startnext-header v-else :lang="locale" :api-url="config.public.scsApiUrl" />
|
|
426
|
-
|
|
427
|
-
<main>
|
|
428
|
-
<slot />
|
|
429
|
-
</main>
|
|
430
|
-
|
|
431
|
-
<div v-if="footerHtml" v-html="footerHtml" style="display: contents" />
|
|
432
|
-
<startnext-footer v-else :lang="locale" :api-url="config.public.scsApiUrl" />
|
|
433
|
-
</template>
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
**Nuxt Config (Custom Elements registrieren):**
|
|
437
|
-
|
|
438
|
-
```ts
|
|
439
|
-
// nuxt.config.ts
|
|
440
|
-
export default defineNuxtConfig({
|
|
441
|
-
vue: {
|
|
442
|
-
compilerOptions: {
|
|
443
|
-
isCustomElement: (tag) => tag.startsWith('startnext-'),
|
|
444
|
-
},
|
|
445
|
-
},
|
|
446
|
-
runtimeConfig: {
|
|
447
|
-
public: {
|
|
448
|
-
scsApiUrl: process.env.SCS_API_URL || 'https://scs-api.vercel.app',
|
|
449
|
-
},
|
|
450
|
-
},
|
|
451
|
-
});
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
### PHP / Vanilla HTML
|
|
455
|
-
|
|
456
|
-
```php
|
|
457
|
-
<?php
|
|
458
|
-
$apiUrl = 'https://scs-api.vercel.app';
|
|
459
|
-
$lang = 'de';
|
|
460
|
-
|
|
461
|
-
$headerHtml = @file_get_contents("$apiUrl/api/header/render?lang=$lang&large-animation");
|
|
462
|
-
$footerHtml = @file_get_contents("$apiUrl/api/footer/render?lang=$lang");
|
|
463
|
-
?>
|
|
464
|
-
<!DOCTYPE html>
|
|
465
|
-
<html lang="<?= $lang ?>">
|
|
466
|
-
<head>
|
|
467
|
-
<script type="module" src="https://unpkg.com/@startnext/chrome@latest/dist/index.js"></script>
|
|
468
|
-
</head>
|
|
469
|
-
<body>
|
|
470
|
-
<?= $headerHtml ?: '<startnext-header api-url="' . $apiUrl . '"></startnext-header>' ?>
|
|
471
|
-
|
|
472
|
-
<main><!-- Content --></main>
|
|
473
|
-
|
|
474
|
-
<?= $footerHtml ?: '<startnext-footer api-url="' . $apiUrl . '"></startnext-footer>' ?>
|
|
475
|
-
</body>
|
|
476
|
-
</html>
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
### Edge Side Includes (ESI)
|
|
480
|
-
|
|
481
|
-
```html
|
|
482
|
-
<!-- Varnish / CDN ESI -->
|
|
483
|
-
<esi:try>
|
|
484
|
-
<esi:attempt>
|
|
485
|
-
<esi:include src="/api/header/render?lang=de&large-animation" />
|
|
486
|
-
</esi:attempt>
|
|
487
|
-
<esi:except>
|
|
488
|
-
<startnext-header api-url="https://scs-api.vercel.app"></startnext-header>
|
|
489
|
-
</esi:except>
|
|
490
|
-
</esi:try>
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
---
|
|
494
|
-
|
|
495
|
-
## Render Entry Point (Server-seitig)
|
|
496
|
-
|
|
497
|
-
Das Paket exportiert einen server-sicheren Subpath `@startnext/chrome/render` mit reinen String-Funktionen (keine Browser-Abhaengigkeiten):
|
|
498
|
-
|
|
499
|
-
```javascript
|
|
500
|
-
import {
|
|
501
|
-
renderHeader, renderBurgerItems, renderUserItems,
|
|
502
|
-
renderFooter,
|
|
503
|
-
headerCSS, footerCSS,
|
|
504
|
-
toHeaderRenderData, toFooterRenderData,
|
|
505
|
-
getUiString, getIcon,
|
|
506
|
-
} from '@startnext/chrome/render';
|
|
507
|
-
```
|
|
508
|
-
|
|
509
|
-
| Export | Beschreibung |
|
|
510
|
-
|--------|-------------|
|
|
511
|
-
| `renderHeader(props)` | Header Shadow DOM HTML |
|
|
512
|
-
| `renderBurgerItems(data, expandedSections)` | Burger-Menue Items HTML |
|
|
513
|
-
| `renderUserItems(data, expandedSections)` | User-Menue Items HTML |
|
|
514
|
-
| `renderFooter(data)` | Footer Shadow DOM HTML |
|
|
515
|
-
| `headerCSS`, `footerCSS`, `resetCSS` | CSS als Strings |
|
|
516
|
-
| `getIcon(name, size?)` | SVG-Icon als String |
|
|
517
|
-
| `getUiString(key, lang)` | UI-String (ARIA Labels etc.) |
|
|
518
|
-
| `toHeaderRenderData(apiData)` | API-Daten -> HeaderData (fuegt stub `theme` hinzu) |
|
|
519
|
-
| `toFooterRenderData(apiData)` | API-Daten -> FooterData (fuegt stub `theme` hinzu) |
|
|
520
|
-
| `renderHeaderCrawlerNav(data)` | Crawler-`<nav>` fuer Header (optional, fuer Client-Side Rendering) |
|
|
521
|
-
| `renderFooterCrawlerNav(data)` | Crawler-`<nav>` fuer Footer (optional, fuer Client-Side Rendering) |
|
|
522
|
-
|
|
523
172
|
## Browser Support
|
|
524
173
|
|
|
525
|
-
- Chrome/Edge 90+
|
|
526
|
-
- Firefox 88+
|
|
527
|
-
- Safari 14+
|
|
174
|
+
- Chrome/Edge 90+
|
|
175
|
+
- Firefox 88+
|
|
176
|
+
- Safari 14+
|
|
528
177
|
|
|
529
178
|
## Development
|
|
530
179
|
|
|
@@ -542,7 +191,7 @@ pnpm --filter @startnext/chrome build
|
|
|
542
191
|
pnpm --filter @startnext/chrome lint
|
|
543
192
|
```
|
|
544
193
|
|
|
545
|
-
Demo-Seite
|
|
194
|
+
Demo-Seite öffnen: `packages/sn-chrome-web-component/examples/vanilla/index.html`
|
|
546
195
|
|
|
547
196
|
## Architecture
|
|
548
197
|
|
|
@@ -552,41 +201,36 @@ Jede Component ist modular in drei Dateien aufgeteilt:
|
|
|
552
201
|
src/
|
|
553
202
|
├── components/
|
|
554
203
|
│ ├── base/
|
|
555
|
-
│ │ ├── BaseComponent.ts # Abstrakte Basis (Shadow DOM,
|
|
204
|
+
│ │ ├── BaseComponent.ts # Abstrakte Basis (Shadow DOM, Events, Theming)
|
|
556
205
|
│ │ ├── icons.ts # SVG-Icon-Registry
|
|
557
206
|
│ │ └── styles.ts # Shared Reset-CSS
|
|
558
207
|
│ ├── header/
|
|
559
|
-
│ │ ├── StartnextHeader.ts # Logik (Scroll, Drawers, Lottie, Events
|
|
208
|
+
│ │ ├── StartnextHeader.ts # Logik (Scroll, Drawers, Lottie, Events)
|
|
560
209
|
│ │ ├── header.css.ts # CSS als Template-Literal-Export
|
|
561
210
|
│ │ └── header.template.ts # Render-Funktionen (HTML-Strings)
|
|
562
211
|
│ └── footer/
|
|
563
|
-
│ ├── StartnextFooter.ts # Logik (Render, Events
|
|
212
|
+
│ ├── StartnextFooter.ts # Logik (Render, Events)
|
|
564
213
|
│ ├── footer.css.ts # CSS als Template-Literal-Export
|
|
565
214
|
│ └── footer.template.ts # Render-Funktion (HTML-String)
|
|
566
215
|
├── data/
|
|
567
|
-
│
|
|
568
|
-
│ └── uiStrings.ts # UI-Strings (ARIA Labels, de/en)
|
|
216
|
+
│ └── mockData.ts # Mock-Daten
|
|
569
217
|
├── types/
|
|
570
218
|
│ └── index.ts # Alle TypeScript-Interfaces
|
|
571
|
-
|
|
572
|
-
└── render.ts # Server-Entry (reine String-Funktionen, kein Browser)
|
|
219
|
+
└── index.ts # Barrel-Export
|
|
573
220
|
```
|
|
574
221
|
|
|
575
222
|
**Konventionen:**
|
|
576
|
-
- CSS in `*.css.ts` als exportierter Template-Literal-String (kein extra Build-Plugin
|
|
577
|
-
- Templates in `*.template.ts` als reine Funktionen die HTML-Strings
|
|
578
|
-
- Logik in der Hauptdatei: Event-Handling, State, Lifecycle
|
|
579
|
-
- BEM-Naming
|
|
580
|
-
- Shadow DOM (open mode), kein Virtual DOM
|
|
581
|
-
|
|
582
|
-
**Zwei Entry Points:**
|
|
583
|
-
- `src/index.ts` -> `dist/index.js` (ESM) + `dist/index.umd.js` (UMD) — Browser, registriert Custom Elements
|
|
584
|
-
- `src/render.ts` -> `dist/render.js` (ESM) — Server, reine String-Funktionen ohne Browser-Abhaengigkeiten
|
|
223
|
+
- CSS in `*.css.ts` als exportierter Template-Literal-String (kein extra Build-Plugin nötig)
|
|
224
|
+
- Templates in `*.template.ts` als reine Funktionen die HTML-Strings zurückgeben
|
|
225
|
+
- Logik in der Hauptdatei: Event-Handling, State, Lifecycle
|
|
226
|
+
- BEM-Naming für CSS-Klassen (`headbar__left`, `drawer__item--highlighted`)
|
|
227
|
+
- Shadow DOM (open mode), kein Virtual DOM
|
|
585
228
|
|
|
586
229
|
## Monorepo Structure
|
|
587
230
|
|
|
588
231
|
```
|
|
589
232
|
packages/
|
|
590
|
-
scs-web-component/
|
|
591
|
-
scs-api/
|
|
233
|
+
scs-web-component/ ← this package
|
|
234
|
+
scs-api/ ← Express API service
|
|
592
235
|
```
|
|
236
|
+
|
|
@@ -5,8 +5,6 @@ import type { ThemeVariables } from '../../types/index.js';
|
|
|
5
5
|
*/
|
|
6
6
|
export declare abstract class BaseComponent extends HTMLElement {
|
|
7
7
|
protected shadow: ShadowRoot;
|
|
8
|
-
/** True when Declarative Shadow DOM was present — first render should hydrate, not replace. */
|
|
9
|
-
protected isHydrating: boolean;
|
|
10
8
|
constructor();
|
|
11
9
|
/** Emit a typed CustomEvent that bubbles and is composed (crosses shadow DOM). */
|
|
12
10
|
protected emit<T>(name: string, detail: T): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseComponent.d.ts","sourceRoot":"","sources":["../../../src/components/base/BaseComponent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;GAGG;AACH,8BAAsB,aAAc,SAAQ,WAAW;IACrD,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC
|
|
1
|
+
{"version":3,"file":"BaseComponent.d.ts","sourceRoot":"","sources":["../../../src/components/base/BaseComponent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;GAGG;AACH,8BAAsB,aAAc,SAAQ,WAAW;IACrD,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC;;IAO7B,kFAAkF;IAClF,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,OAAO;IAWnD,mEAAmE;IACnE,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,UAAQ,GAAG,OAAO;IAM9D,6DAA6D;IAC7D,SAAS,CAAC,UAAU,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAMjD,kDAAkD;IAClD,SAAS,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB;CAKrD"}
|
|
@@ -4,6 +4,8 @@ export declare class StartnextFooter extends BaseComponent {
|
|
|
4
4
|
private data;
|
|
5
5
|
/** Cached API data per language. */
|
|
6
6
|
private apiCache;
|
|
7
|
+
/** Languages currently being fetched — prevents duplicate in-flight requests. */
|
|
8
|
+
private fetchingLangs;
|
|
7
9
|
/** Bound handler for document-level language-change events. */
|
|
8
10
|
private _langSyncHandler;
|
|
9
11
|
private get currentLang();
|
|
@@ -15,9 +17,6 @@ export declare class StartnextFooter extends BaseComponent {
|
|
|
15
17
|
private teardownLangSync;
|
|
16
18
|
/** Return cached API data if available, otherwise defaults (theme only). */
|
|
17
19
|
private getFooterData;
|
|
18
|
-
/** Parse SSR-embedded data to populate the API cache during hydration.
|
|
19
|
-
* Eliminates the need for a client-side fetch when SSR HTML is used. */
|
|
20
|
-
private parseSsrProps;
|
|
21
20
|
/** If api-url is set, fetch footer data from API and re-render. */
|
|
22
21
|
private loadApiData;
|
|
23
22
|
private renderLightDomNav;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StartnextFooter.d.ts","sourceRoot":"","sources":["../../../src/components/footer/StartnextFooter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAMzD,qBAAa,eAAgB,SAAQ,aAAa;IAChD,MAAM,CAAC,kBAAkB,WAAoC;IAE7D,OAAO,CAAC,IAAI,CAAc;IAC1B,oCAAoC;IACpC,OAAO,CAAC,QAAQ,CAAiC;IACjD,+DAA+D;IAC/D,OAAO,CAAC,gBAAgB,CAAqC;IAE7D,OAAO,KAAK,WAAW,GAEtB;IAED,OAAO,KAAK,eAAe,GAE1B;IAED,iBAAiB,IAAI,IAAI;
|
|
1
|
+
{"version":3,"file":"StartnextFooter.d.ts","sourceRoot":"","sources":["../../../src/components/footer/StartnextFooter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAMzD,qBAAa,eAAgB,SAAQ,aAAa;IAChD,MAAM,CAAC,kBAAkB,WAAoC;IAE7D,OAAO,CAAC,IAAI,CAAc;IAC1B,oCAAoC;IACpC,OAAO,CAAC,QAAQ,CAAiC;IACjD,iFAAiF;IACjF,OAAO,CAAC,aAAa,CAAqB;IAC1C,+DAA+D;IAC/D,OAAO,CAAC,gBAAgB,CAAqC;IAE7D,OAAO,KAAK,WAAW,GAEtB;IAED,OAAO,KAAK,eAAe,GAE1B;IAED,iBAAiB,IAAI,IAAI;IASzB,oBAAoB,IAAI,IAAI;IAK5B,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAe5C,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,gBAAgB;IAOxB,4EAA4E;IAC5E,OAAO,CAAC,aAAa;IAMrB,mEAAmE;YACrD,WAAW;IA2BzB,OAAO,CAAC,iBAAiB;IAiCzB,OAAO,CAAC,MAAM;IAWd,OAAO,CAAC,YAAY;CAUrB"}
|
|
@@ -12,6 +12,8 @@ export declare class StartnextHeader extends BaseComponent {
|
|
|
12
12
|
private pendingRaf;
|
|
13
13
|
/** Cached API data per language, so we don't re-fetch on every re-render. */
|
|
14
14
|
private apiCache;
|
|
15
|
+
/** Languages currently being fetched — prevents duplicate in-flight requests. */
|
|
16
|
+
private fetchingLangs;
|
|
15
17
|
private handleScrollBound;
|
|
16
18
|
private handleKeydownBound;
|
|
17
19
|
private handleResizeBound;
|
|
@@ -19,15 +21,13 @@ export declare class StartnextHeader extends BaseComponent {
|
|
|
19
21
|
static get observedAttributes(): string[];
|
|
20
22
|
private _skipCallback;
|
|
21
23
|
private get currentLang();
|
|
24
|
+
private get requestedLanguages();
|
|
22
25
|
/** True when header should show white text (dark page / hero background). */
|
|
23
26
|
private get isLightHeader();
|
|
24
27
|
private ui;
|
|
25
28
|
connectedCallback(): void;
|
|
26
29
|
/** Return cached API data if available, otherwise defaults (theme only). */
|
|
27
30
|
private getHeaderData;
|
|
28
|
-
/** Parse SSR-embedded data to populate the API cache during hydration.
|
|
29
|
-
* Eliminates the need for a client-side fetch when SSR HTML is used. */
|
|
30
|
-
private parseSsrProps;
|
|
31
31
|
/** If api-url is set, fetch header data from API and re-render. */
|
|
32
32
|
private loadApiData;
|
|
33
33
|
disconnectedCallback(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StartnextHeader.d.ts","sourceRoot":"","sources":["../../../src/components/header/StartnextHeader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAWzD,qBAAa,eAAgB,SAAQ,aAAa;IAChD,OAAO,CAAC,IAAI,CAAc;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,UAAU,CAAuB;IACzC,6EAA6E;IAC7E,OAAO,CAAC,QAAQ,CAAiC;
|
|
1
|
+
{"version":3,"file":"StartnextHeader.d.ts","sourceRoot":"","sources":["../../../src/components/header/StartnextHeader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAWzD,qBAAa,eAAgB,SAAQ,aAAa;IAChD,OAAO,CAAC,IAAI,CAAc;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,UAAU,CAAuB;IACzC,6EAA6E;IAC7E,OAAO,CAAC,QAAQ,CAAiC;IACjD,iFAAiF;IACjF,OAAO,CAAC,aAAa,CAAqB;IAE1C,OAAO,CAAC,iBAAiB,CAAgC;IACzD,OAAO,CAAC,kBAAkB,CAAiC;IAC3D,OAAO,CAAC,iBAAiB,CAAgC;IACzD,OAAO,CAAC,mBAAmB,CAAkC;IAE7D,MAAM,KAAK,kBAAkB,IAAI,MAAM,EAAE,CAExC;IAED,OAAO,CAAC,aAAa,CAAS;IAE9B,OAAO,KAAK,WAAW,GAEtB;IAED,OAAO,KAAK,kBAAkB,GAI7B;IAED,6EAA6E;IAC7E,OAAO,KAAK,aAAa,GAGxB;IAED,OAAO,CAAC,EAAE;IAIV,iBAAiB,IAAI,IAAI;IAgBzB,4EAA4E;IAC5E,OAAO,CAAC,aAAa;IAMrB,mEAAmE;YACrD,WAAW;IAiCzB,oBAAoB,IAAI,IAAI;IAU5B,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAa5C,gEAAgE;IAChE,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,UAAU;IA0BlB,OAAO,CAAC,YAAY;IAgDpB,OAAO,CAAC,mBAAmB;IAoC3B,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,oBAAoB;IAI5B,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,aAAa;IAmBrB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,cAAc;IA0BtB,OAAO,CAAC,iBAAiB;IAsDzB,OAAO,CAAC,MAAM;IAsCd,OAAO,CAAC,YAAY;CAkErB"}
|