@conciso/design-system-angular 0.0.0-bootstrap.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/LICENSE +21 -0
- package/NOTICE +15 -0
- package/README.md +183 -0
- package/fesm2022/conciso-design-system-angular.mjs +7662 -0
- package/fesm2022/conciso-design-system-angular.mjs.map +1 -0
- package/package.json +38 -0
- package/types/conciso-design-system-angular.d.ts +3442 -0
|
@@ -0,0 +1,3442 @@
|
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { TemplateRef } from '@angular/core';
|
|
3
|
+
import { ControlValueAccessor } from '@angular/forms';
|
|
4
|
+
import * as _conciso_design_system_angular from '@conciso/design-system-angular';
|
|
5
|
+
import { SafeHtml } from '@angular/platform-browser';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Die vier Markenbereiche des Conciso Design Systems. Sie steuern in der CSS-Schicht
|
|
9
|
+
* die Bereichsfarben (Tokens --co-* / --ki-* / --es-* / --wo-*) und werden je nach
|
|
10
|
+
* Komponente entweder über eine Modifier-Klasse (z. B. .btn-co) oder ein
|
|
11
|
+
* data-area-Attribut (z. B. .badge[data-area="co"]) angewandt.
|
|
12
|
+
*/
|
|
13
|
+
type CdsArea = 'co' | 'ki' | 'es' | 'wo';
|
|
14
|
+
declare const CDS_AREAS: CdsArea[];
|
|
15
|
+
declare const CDS_AREA_LABELS: Record<CdsArea, string>;
|
|
16
|
+
|
|
17
|
+
/** Drei Theme-Modi. „system“ folgt der OS-Einstellung (prefers-color-scheme). */
|
|
18
|
+
type CdsThemeMode = 'light' | 'dark' | 'system';
|
|
19
|
+
/** Reihenfolge (Cycle-Button, Segment, Dropdown). */
|
|
20
|
+
declare const CDS_THEME_ORDER: CdsThemeMode[];
|
|
21
|
+
/** Icon-Name je Modus. Registriert über die zentrale Icon-Registry (lib/icons/cds-icons);
|
|
22
|
+
* für diese drei Chrome-Icons hat das DS-Set kein eigenes Glyph → Heroicons. */
|
|
23
|
+
declare const CDS_THEME_ICON: Record<CdsThemeMode, string>;
|
|
24
|
+
/** Deutsches Label je Modus. */
|
|
25
|
+
declare const CDS_THEME_LABEL: Record<CdsThemeMode, string>;
|
|
26
|
+
/** tri = alle drei, binär = ohne „System“. Basis des `showSystem`-Parameters. */
|
|
27
|
+
declare function cdsThemeModes(showSystem: boolean): CdsThemeMode[];
|
|
28
|
+
declare const themeStore: {
|
|
29
|
+
/** Readonly-Signal des aktuellen Modus (Komponenten lesen hierüber). */
|
|
30
|
+
mode: _angular_core.Signal<CdsThemeMode>;
|
|
31
|
+
/** Setzen + anwenden + Abonnenten benachrichtigen (z. B. Toolbar-Sync). */
|
|
32
|
+
set(mode: CdsThemeMode): void;
|
|
33
|
+
/** Wie set(), aber OHNE Broadcast — für Toolbar→Store, um eine Emit-Rückkopplung
|
|
34
|
+
* zu vermeiden (die Toolbar hat den Wert ja bereits). */
|
|
35
|
+
setSilent(mode: CdsThemeMode): void;
|
|
36
|
+
/** Auf Modus-Änderungen hören (gibt eine Abmelde-Funktion zurück). */
|
|
37
|
+
subscribe(cb: (m: CdsThemeMode) => void): () => void;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Dünner Angular-Service über der modul-globalen Quelle — damit die Komponenten
|
|
41
|
+
* wie gewohnt `inject(ThemeModeService)` nutzen können. Setzt/entfernt letztlich
|
|
42
|
+
* `data-theme="dark"` am <html> (genau wie der DS es erwartet). Alle Switcher UND
|
|
43
|
+
* der globale Theme-Toolbar-Schalter bleiben so synchron.
|
|
44
|
+
*/
|
|
45
|
+
declare class ThemeModeService {
|
|
46
|
+
readonly mode: _angular_core.Signal<CdsThemeMode>;
|
|
47
|
+
set(mode: CdsThemeMode): void;
|
|
48
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ThemeModeService, never>;
|
|
49
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<any>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Stil-Varianten des Buttons. `filled-on-band` ist der invertierte Filled-Button
|
|
54
|
+
* für farbige Bereichs-Bänder (Doku: „On-Band-Modifier“) — er wird hier als
|
|
55
|
+
* eigener Variant-Wert geführt statt als abhängiges Flag, da das Invertieren nur
|
|
56
|
+
* in Kombination mit `filled` je sinnvoll ist.
|
|
57
|
+
*/
|
|
58
|
+
type CdsButtonVariant = 'filled' | 'tonal' | 'elevated' | 'outlined' | 'text' | 'filled-on-band';
|
|
59
|
+
type CdsButtonSize = 'sm' | 'md' | 'lg';
|
|
60
|
+
/**
|
|
61
|
+
* Button — dünner Angular-Wrapper um die bestehende `.btn`-CSS-Familie.
|
|
62
|
+
*
|
|
63
|
+
* Erfindet KEINE Styles: setzt ausschließlich die im Design System vorhandenen
|
|
64
|
+
* Klassen (.btn, .btn-filled/.btn-tonal/.btn-elevated/.btn-outlined/.btn-text,
|
|
65
|
+
* .btn-sm/.btn-lg, .btn-co/.btn-ki/.btn-es/.btn-wo, .btn-full, .btn-on-band)
|
|
66
|
+
* zusammen. Siehe css/components.css → „Buttons“. Der Klick wird als `clicked`
|
|
67
|
+
* ausgegeben (für Nutzung außerhalb reiner Formular-Submits).
|
|
68
|
+
*/
|
|
69
|
+
declare class ButtonComponent {
|
|
70
|
+
/** Sichtbarer Text des Buttons. */
|
|
71
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
72
|
+
/** Visuelle Variante → .btn-filled / .btn-tonal / .btn-elevated / .btn-outlined /
|
|
73
|
+
* .btn-text; `filled-on-band` → .btn-filled + .btn-on-band (invertiert). */
|
|
74
|
+
readonly variant: _angular_core.InputSignal<CdsButtonVariant>;
|
|
75
|
+
/** Markenbereich → .btn-co / .btn-ki / .btn-es / .btn-wo */
|
|
76
|
+
readonly area: _angular_core.InputSignal<CdsArea>;
|
|
77
|
+
/** Größe → .btn-sm / (md = Default) / .btn-lg */
|
|
78
|
+
readonly size: _angular_core.InputSignal<CdsButtonSize>;
|
|
79
|
+
/** Volle Breite → .btn-full (Host wird block, damit 100% greifen). */
|
|
80
|
+
readonly full: _angular_core.InputSignal<boolean>;
|
|
81
|
+
readonly disabled: _angular_core.InputSignal<boolean>;
|
|
82
|
+
readonly type: _angular_core.InputSignal<"button" | "submit" | "reset">;
|
|
83
|
+
/** Klick auf den Button (feuert nicht, wenn `disabled`). */
|
|
84
|
+
readonly clicked: _angular_core.OutputEmitterRef<MouseEvent>;
|
|
85
|
+
/** @internal */
|
|
86
|
+
protected readonly classes: _angular_core.Signal<string>;
|
|
87
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ButtonComponent, never>;
|
|
88
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ButtonComponent, "cds-button", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "full": { "alias": "full"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; }, { "clicked": "clicked"; }, never, never, true, never>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface CdsNavSubItem {
|
|
92
|
+
label: string;
|
|
93
|
+
href: string;
|
|
94
|
+
}
|
|
95
|
+
interface CdsNavItem {
|
|
96
|
+
label: string;
|
|
97
|
+
href?: string;
|
|
98
|
+
sub?: CdsNavSubItem[];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Topnav — Wrapper um `.ep-topnav` aus css/components.css → „Topnav“.
|
|
102
|
+
*
|
|
103
|
+
* Customer-Navigation: Logo, Top-Level-Links mit optionalem Klapp-Submenü
|
|
104
|
+
* (.ep-nav-has-sub / .ep-nav-sub, aria-expanded), rechts gebündelte Aktionen
|
|
105
|
+
* (Such-Popover .ep-nav-search + Theme-Cycle-Button) und ein CTA-Button.
|
|
106
|
+
* Disclosure-Logik in Angular: nur ein Menü offen, Escape und Außenklick schließen.
|
|
107
|
+
*
|
|
108
|
+
* Der Theme-Umschalter ist fest der Cycle-Button (cds-theme-cycle); ob er auch
|
|
109
|
+
* „System“ anbietet, steuert `showSystemTheme` (durchgereicht an dessen showSystem).
|
|
110
|
+
*/
|
|
111
|
+
declare class TopnavComponent {
|
|
112
|
+
private readonly host;
|
|
113
|
+
private readonly document;
|
|
114
|
+
private readonly uid;
|
|
115
|
+
/** @internal */
|
|
116
|
+
protected readonly searchId: string;
|
|
117
|
+
/** @internal */
|
|
118
|
+
protected readonly navId: string;
|
|
119
|
+
readonly logo: _angular_core.InputSignal<string>;
|
|
120
|
+
/** Optionales Logo-Bild/-Icon (URL oder Data-URI). Gesetzt → statt des Text-Logos.
|
|
121
|
+
* Doku: immer SVG (logo-conciso.svg), Default-Variante für helle Hintergründe. */
|
|
122
|
+
readonly logoSrc: _angular_core.InputSignal<string | undefined>;
|
|
123
|
+
/** Optionale helle Logo-Variante für den Dark Mode (Doku: logo-conciso-light.svg).
|
|
124
|
+
* Gesetzt → Theme-Swap via .logo-themed-* (schaltet über [data-theme] um);
|
|
125
|
+
* sonst wird logoSrc in beiden Themes gezeigt. */
|
|
126
|
+
readonly logoDarkSrc: _angular_core.InputSignal<string | undefined>;
|
|
127
|
+
/** Alt-Text des Logo-Bilds (Fallback: der Text aus `logo`). */
|
|
128
|
+
readonly logoAlt: _angular_core.InputSignal<string>;
|
|
129
|
+
readonly ctaLabel: _angular_core.InputSignal<string>;
|
|
130
|
+
/** Suche (Icon + Popover) rechts anzeigen. */
|
|
131
|
+
readonly showSearch: _angular_core.InputSignal<boolean>;
|
|
132
|
+
/** Kontakt-/CTA-Button anzeigen. */
|
|
133
|
+
readonly showCta: _angular_core.InputSignal<boolean>;
|
|
134
|
+
/** Bietet der Theme-Cycle-Button im Header auch „System“ an (tri) oder nur Hell/Dunkel? */
|
|
135
|
+
readonly showSystemTheme: _angular_core.InputSignal<boolean>;
|
|
136
|
+
/** Href des aktuell aktiven Eintrags (Single Source of Truth). Nur der Eintrag
|
|
137
|
+
* — Top-Level ODER Sub — mit passendem href erhält aria-current="page". Dadurch
|
|
138
|
+
* kann strukturell höchstens EINER aktiv sein (statt mehrerer current-Flags). */
|
|
139
|
+
readonly activeHref: _angular_core.InputSignal<string | undefined>;
|
|
140
|
+
readonly links: _angular_core.InputSignal<CdsNavItem[]>;
|
|
141
|
+
/** @internal */
|
|
142
|
+
protected readonly openIndex: _angular_core.WritableSignal<number>;
|
|
143
|
+
/** @internal */
|
|
144
|
+
protected readonly searchOpen: _angular_core.WritableSignal<boolean>;
|
|
145
|
+
/**
|
|
146
|
+
* Mobile-Menü offen? Schaltet .nav-open am Header (→ .ep-nav-links sichtbar).
|
|
147
|
+
*
|
|
148
|
+
* @internal
|
|
149
|
+
*/
|
|
150
|
+
protected readonly navOpen: _angular_core.WritableSignal<boolean>;
|
|
151
|
+
/**
|
|
152
|
+
* Eindeutige ID des Submenüs zu Eintrag i (Toggle aria-controls ↔ .ep-nav-sub).
|
|
153
|
+
*
|
|
154
|
+
* @internal
|
|
155
|
+
*/
|
|
156
|
+
protected subId(i: number): string;
|
|
157
|
+
/** @internal */
|
|
158
|
+
protected toggleSub(i: number): void;
|
|
159
|
+
/** @internal */
|
|
160
|
+
protected toggleSearch(): void;
|
|
161
|
+
/** @internal */
|
|
162
|
+
protected toggleNav(): void;
|
|
163
|
+
/** @internal */
|
|
164
|
+
protected closeAll(): void;
|
|
165
|
+
/** @internal */
|
|
166
|
+
protected onDocumentClick(event: MouseEvent): void;
|
|
167
|
+
/** @internal */
|
|
168
|
+
protected onEscape(): void;
|
|
169
|
+
/**
|
|
170
|
+
* Wie `closeAll()`, gibt den Fokus aber an den öffnenden Toggle zurück (Submenü-
|
|
171
|
+
* bzw. Such-Toggle) — NUR wenn der Fokus beim Schließen tatsächlich innerhalb des
|
|
172
|
+
* schließenden Bereichs lag. `css/components.css:1148` setzt `.ep-nav-sub{display:
|
|
173
|
+
* none}`, `:1189` dasselbe für `.ep-nav-search-pop`: das fokussierte Element
|
|
174
|
+
* verschwindet damit aus dem Fokus-Baum und der Fokus fiele sonst ans `<body>`
|
|
175
|
+
* (WCAG 2.4.3). Nur für den Escape-Pfad gedacht.
|
|
176
|
+
*
|
|
177
|
+
* @internal
|
|
178
|
+
*/
|
|
179
|
+
private closeWithFocusReturn;
|
|
180
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TopnavComponent, never>;
|
|
181
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<TopnavComponent, "cds-topnav", never, { "logo": { "alias": "logo"; "required": false; "isSignal": true; }; "logoSrc": { "alias": "logoSrc"; "required": false; "isSignal": true; }; "logoDarkSrc": { "alias": "logoDarkSrc"; "required": false; "isSignal": true; }; "logoAlt": { "alias": "logoAlt"; "required": false; "isSignal": true; }; "ctaLabel": { "alias": "ctaLabel"; "required": false; "isSignal": true; }; "showSearch": { "alias": "showSearch"; "required": false; "isSignal": true; }; "showCta": { "alias": "showCta"; "required": false; "isSignal": true; }; "showSystemTheme": { "alias": "showSystemTheme"; "required": false; "isSignal": true; }; "activeHref": { "alias": "activeHref"; "required": false; "isSignal": true; }; "links": { "alias": "links"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Theme-Cycle-Button — ein einzelner Icon-Button (Stil `.ep-nav-icon-btn` aus der
|
|
186
|
+
* Topnav), der bei Klick durch die Modi zyklt. Das Icon zeigt den aktuellen Modus.
|
|
187
|
+
* Vorgesehener Einsatz: im Header. Icons über die zentrale Registry (lib/icons/cds-icons).
|
|
188
|
+
*
|
|
189
|
+
* `showSystem` schaltet zwischen tri (Hell→Dunkel→System) und binär (Hell→Dunkel).
|
|
190
|
+
*/
|
|
191
|
+
declare class ThemeCycleComponent {
|
|
192
|
+
/** true → Hell/Dunkel/System, false → nur Hell/Dunkel. */
|
|
193
|
+
readonly showSystem: _angular_core.InputSignal<boolean>;
|
|
194
|
+
/** @internal */
|
|
195
|
+
protected readonly svc: ThemeModeService;
|
|
196
|
+
/** @internal */
|
|
197
|
+
protected readonly icon: _angular_core.Signal<string>;
|
|
198
|
+
/** @internal */
|
|
199
|
+
protected readonly label: _angular_core.Signal<string>;
|
|
200
|
+
/** @internal */
|
|
201
|
+
protected next(): void;
|
|
202
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ThemeCycleComponent, never>;
|
|
203
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ThemeCycleComponent, "cds-theme-cycle", never, { "showSystem": { "alias": "showSystem"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Bereichs-Badge — passive Bereichs-Kennzeichnung auf Basis von `.badge` +
|
|
208
|
+
* `[data-area]` aus css/components.css.
|
|
209
|
+
*
|
|
210
|
+
* Ordnet ein Element einer der vier Brand Areas zu (50er-Grund, 800er-Text).
|
|
211
|
+
* Nicht interaktiv. Für Zustände die Status-Badge, für einen redaktionellen
|
|
212
|
+
* Anker im Lesefluss die Pill nutzen; für interaktive Filter den Chip.
|
|
213
|
+
*/
|
|
214
|
+
declare class AreaBadgeComponent {
|
|
215
|
+
/** Sichtbarer Text der Badge. */
|
|
216
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
217
|
+
/** Brand Area → data-area (bestimmt die Bereichsfarbe). */
|
|
218
|
+
readonly area: _angular_core.InputSignal<CdsArea>;
|
|
219
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<AreaBadgeComponent, never>;
|
|
220
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<AreaBadgeComponent, "cds-area-badge", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Status-Töne der Badge-Klassen (.badge-ok / .badge-warn / .badge-err / .badge-neu). */
|
|
224
|
+
type CdsBadgeTone = 'ok' | 'warn' | 'err' | 'neu';
|
|
225
|
+
/**
|
|
226
|
+
* Status-Badge — passive Zustands-Kennzeichnung auf Basis von `.badge` +
|
|
227
|
+
* `.badge-{ok|warn|err|neu}` aus css/components.css.
|
|
228
|
+
*
|
|
229
|
+
* Trägt einen Zustand über semantische Farben (OK, Warnung, Fehler, Neutral) —
|
|
230
|
+
* z. B. „Live“, „Beta“, „Deprecated“, „Draft“. Nicht interaktiv. Für die
|
|
231
|
+
* Zuordnung zu einer Brand Area die Bereichs-Badge, für einen redaktionellen
|
|
232
|
+
* Anker im Lesefluss die Pill nutzen; für interaktive Filter den Chip.
|
|
233
|
+
*/
|
|
234
|
+
declare class StatusBadgeComponent {
|
|
235
|
+
/** Sichtbarer Text der Badge. */
|
|
236
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
237
|
+
/** Status-Ton → semantische Farbe. */
|
|
238
|
+
readonly tone: _angular_core.InputSignal<CdsBadgeTone>;
|
|
239
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<StatusBadgeComponent, never>;
|
|
240
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<StatusBadgeComponent, "cds-status-badge", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Chip — Wrapper um `.chip` aus css/components.css → „Badges & Chips“.
|
|
245
|
+
*
|
|
246
|
+
* Interaktiver Filter-Chip auf Basis von `aria-pressed` (.chip[aria-pressed],
|
|
247
|
+
* optional area-aware über [data-area]). Klick schaltet den Zustand um.
|
|
248
|
+
*
|
|
249
|
+
* Chips sind interaktive Elemente (Filter/Einstellungen), KEINE statischen Tags.
|
|
250
|
+
* Für nicht-interaktive Bereichs-Labels die Badge- bzw. Pill-Komponente nutzen.
|
|
251
|
+
*/
|
|
252
|
+
declare class ChipComponent {
|
|
253
|
+
/** Sichtbarer Text des Chips. */
|
|
254
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
255
|
+
/** Markenbereich → data-area (area-aware Outline). */
|
|
256
|
+
readonly area: _angular_core.InputSignal<CdsArea | undefined>;
|
|
257
|
+
/** Gedrückt/aktiv → aria-pressed. Two-Way (`[(pressed)]`) + pressedChange via model(). */
|
|
258
|
+
readonly pressed: _angular_core.ModelSignal<boolean>;
|
|
259
|
+
/** @internal */
|
|
260
|
+
protected toggle(): void;
|
|
261
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ChipComponent, never>;
|
|
262
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ChipComponent, "cds-chip", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; "pressed": { "alias": "pressed"; "required": false; "isSignal": true; }; }, { "pressed": "pressedChange"; }, never, never, true, never>;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Pill — Wrapper um `.pill` aus css/components.css → „Badges & Chips“ (Pill).
|
|
267
|
+
*
|
|
268
|
+
* Passive, redaktionelle Bereichs-Markierung, typografisch als Eyebrow (uppercase,
|
|
269
|
+
* letter-spacing .09em, label-xs) — sitzt typischerweise oberhalb eines Titels und
|
|
270
|
+
* ordnet einen Inhalt einer Brand Area zu. Abgrenzung zur Badge: die Pill ist der
|
|
271
|
+
* thematische „Was für ein Inhalt ist das?“-Anker im Lesefluss; die Badge die
|
|
272
|
+
* punktuelle Status-/Bereichs-Kennzeichnung neben Elementen. Nicht interaktiv.
|
|
273
|
+
*
|
|
274
|
+
* a11y: Der reine Bereichsname klingt im Screenreader-Fluss leicht wie eine
|
|
275
|
+
* Überschrift — daher trägt die Pill ein explizites `aria-label` (Default
|
|
276
|
+
* „Bereich <label>“, überschreibbar), genau wie in der Doku vorgegeben.
|
|
277
|
+
*/
|
|
278
|
+
declare class PillComponent {
|
|
279
|
+
/** Sichtbarer Bereichsname. */
|
|
280
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
281
|
+
/** Brand Area → data-area (Farbton; ohne Angabe greift der Corporate-Default). */
|
|
282
|
+
readonly area: _angular_core.InputSignal<CdsArea>;
|
|
283
|
+
/** aria-label überschreiben; Default „Bereich <label>“. */
|
|
284
|
+
readonly ariaLabel: _angular_core.InputSignal<string | undefined>;
|
|
285
|
+
/** @internal */
|
|
286
|
+
protected readonly computedAriaLabel: _angular_core.Signal<string>;
|
|
287
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<PillComponent, never>;
|
|
288
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<PillComponent, "cds-pill", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* ControlValueAccessor-Kitt OHNE Markup- oder Werttyp-Annahmen: hält NUR, was bei
|
|
293
|
+
* `checkbox`, `radio-group`, `slider`, `scale` (und, über `FieldBase`, den drei
|
|
294
|
+
* Field-Komponenten) wortgleich war — die `onChange`/`onTouched`-Callbacks plus
|
|
295
|
+
* `writeValue`, `registerOnChange`, `registerOnTouched`, `setDisabledState`.
|
|
296
|
+
*
|
|
297
|
+
* Generisch über den Werttyp `T` (boolean bei Checkbox, string bei RadioGroup/
|
|
298
|
+
* FieldBase, number bei Slider/Scale). Die defensive Normalisierung eines
|
|
299
|
+
* eingehenden Formularwerts (Klemmen auf [min, max], `!!`-Cast, `NaN`-Abfang, …)
|
|
300
|
+
* ist komponentenspezifisch und bleibt darum bewusst NICHT hier: `writeValue` ruft
|
|
301
|
+
* den überschreibbaren Hook `normalizeValue` auf (Default: unverändert), die
|
|
302
|
+
* konkrete Komponente überschreibt ihn UND `applyValue`/`applyDisabled` (schreiben
|
|
303
|
+
* in ihr eigenes `model()`-Signal — die Basis kennt dessen Namen nicht).
|
|
304
|
+
*/
|
|
305
|
+
declare abstract class CvaBase<T> implements ControlValueAccessor {
|
|
306
|
+
/** @internal */
|
|
307
|
+
protected onChange: (value: T) => void;
|
|
308
|
+
/** @internal */
|
|
309
|
+
protected onTouched: () => void;
|
|
310
|
+
/**
|
|
311
|
+
* Rohen Formularwert in einen gültigen Komponentenwert übersetzen (Klemmen,
|
|
312
|
+
* Typ-Cast, `NaN`-Abfang, …). Default: unverändert durchreichen.
|
|
313
|
+
*
|
|
314
|
+
* @internal
|
|
315
|
+
*/
|
|
316
|
+
protected normalizeValue(value: T): T;
|
|
317
|
+
/** Normalisierten Wert ins eigene `model()`-Signal der Komponente schreiben. @internal */
|
|
318
|
+
protected abstract applyValue(value: T): void;
|
|
319
|
+
/** `disabled`-Zustand ins eigene `model()`-Signal der Komponente schreiben. @internal */
|
|
320
|
+
protected abstract applyDisabled(disabled: boolean): void;
|
|
321
|
+
/** @internal */
|
|
322
|
+
writeValue(value: T): void;
|
|
323
|
+
/** @internal */
|
|
324
|
+
registerOnChange(fn: (value: T) => void): void;
|
|
325
|
+
/** @internal */
|
|
326
|
+
registerOnTouched(fn: () => void): void;
|
|
327
|
+
/** @internal */
|
|
328
|
+
setDisabledState(isDisabled: boolean): void;
|
|
329
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<CvaBase<any>, never>;
|
|
330
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<CvaBase<any>, never, never, {}, {}, never, never, true, never>;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Gemeinsame Basis der Field-Komponenten (Textfeld / Textbereich / Auswahlfeld).
|
|
335
|
+
*
|
|
336
|
+
* Bündelt die geteilten Inputs, die a11y-Verdrahtung UND die Wert-Anbindung. Als
|
|
337
|
+
* `ControlValueAccessor` binden sich die Felder direkt an Angular-Formulare
|
|
338
|
+
* (`[(ngModel)]`, `formControlName`) — der übliche Weg für eine Komponenten-
|
|
339
|
+
* bibliothek. Zusätzlich ist `value` ein `model()`, sodass ohne Formular auch
|
|
340
|
+
* `[(value)]` und der `valueChange`-Output funktionieren. Den ControlValueAccessor-
|
|
341
|
+
* Kitt (onChange/onTouched, writeValue, registerOnChange/-Touched, setDisabledState)
|
|
342
|
+
* erbt sie von `CvaBase` (`lib/shared/cva-base.directive.ts`) — geteilt mit
|
|
343
|
+
* Checkbox/RadioGroup/Slider/Scale, die dieselbe Boilerplate wortgleich hatten.
|
|
344
|
+
*
|
|
345
|
+
* Angular vererbt Inputs/Logik über eine mit `@Directive()` dekorierte abstrakte
|
|
346
|
+
* Basisklasse — deshalb steht die LOGIK hier. Das gemeinsame MARKUP (Label/Helper/
|
|
347
|
+
* Fehler) wird NICHT vererbt (Angular vererbt keine Templates); dafür teilen sich
|
|
348
|
+
* die Komponenten die FieldShellComponent per Komposition. Den `NG_VALUE_ACCESSOR`-
|
|
349
|
+
* Provider setzt jede konkrete Komponente selbst (forwardRef auf ihre Klasse).
|
|
350
|
+
*/
|
|
351
|
+
declare abstract class FieldBase extends CvaBase<string> {
|
|
352
|
+
/** Sichtbares Label. */
|
|
353
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
354
|
+
/** Optionaler Hilfetext unter dem Feld. */
|
|
355
|
+
readonly helper: _angular_core.InputSignal<string>;
|
|
356
|
+
/** Gesetzt = Fehlerzustand (.has-error + .error-msg). */
|
|
357
|
+
readonly error: _angular_core.InputSignal<string>;
|
|
358
|
+
/** Pflichtfeld → .req-Asterisk + aria-required. */
|
|
359
|
+
readonly required: _angular_core.InputSignal<boolean>;
|
|
360
|
+
/** id für die label/for- und aria-describedby-Verknüpfung. Default eindeutig. */
|
|
361
|
+
readonly fieldId: _angular_core.InputSignal<string>;
|
|
362
|
+
/** Feldwert — Two-Way (`[(value)]`) UND Angular-Forms (ngModel/formControlName). */
|
|
363
|
+
readonly value: _angular_core.ModelSignal<string>;
|
|
364
|
+
/** Deaktiviert; auch über Angular-Forms (setDisabledState) steuerbar. */
|
|
365
|
+
readonly disabled: _angular_core.ModelSignal<boolean>;
|
|
366
|
+
/** @internal */
|
|
367
|
+
protected normalizeValue(value: string): string;
|
|
368
|
+
/** @internal */
|
|
369
|
+
protected applyValue(value: string): void;
|
|
370
|
+
/** @internal */
|
|
371
|
+
protected applyDisabled(disabled: boolean): void;
|
|
372
|
+
/**
|
|
373
|
+
* Vom Template bei Eingabe/Änderung des nativen Controls aufgerufen.
|
|
374
|
+
*
|
|
375
|
+
* @internal
|
|
376
|
+
*/
|
|
377
|
+
protected handleInput(event: Event): void;
|
|
378
|
+
/**
|
|
379
|
+
* Vom Template bei Verlassen des Felds → markiert das Control als „touched“.
|
|
380
|
+
*
|
|
381
|
+
* @internal
|
|
382
|
+
*/
|
|
383
|
+
protected handleBlur(): void;
|
|
384
|
+
/** @internal */
|
|
385
|
+
protected readonly errorId: _angular_core.Signal<string>;
|
|
386
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FieldBase, never>;
|
|
387
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<FieldBase, never, never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "helper": { "alias": "helper"; "required": false; "isSignal": true; }; "error": { "alias": "error"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "fieldId": { "alias": "fieldId"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; }, never, never, true, never>;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Textfeld — einzeiliges `<input>` im `.field`-Gerüst (E-Mail, Text, Tel, … über
|
|
392
|
+
* `type`). Pflichtfelder tragen aria-required; im Fehlerzustand wird die Meldung
|
|
393
|
+
* über aria-describedby/aria-invalid verknüpft — wie docs/index.html.
|
|
394
|
+
*
|
|
395
|
+
* Als `ControlValueAccessor` (siehe FieldBase) direkt an Angular-Formulare
|
|
396
|
+
* anbindbar (`[(ngModel)]`, `formControlName`) bzw. ohne Formular per `[(value)]`.
|
|
397
|
+
*/
|
|
398
|
+
declare class TextFieldComponent extends FieldBase {
|
|
399
|
+
/** input[type] (text, email, tel, …). */
|
|
400
|
+
readonly type: _angular_core.InputSignal<string>;
|
|
401
|
+
readonly placeholder: _angular_core.InputSignal<string>;
|
|
402
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TextFieldComponent, never>;
|
|
403
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<TextFieldComponent, "cds-text-field", never, { "type": { "alias": "type"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Textbereich — mehrzeiliges `<textarea>` im `.field`-Gerüst. Gleiche a11y-
|
|
408
|
+
* Verdrahtung wie das Textfeld (aria-required/-invalid/-describedby).
|
|
409
|
+
*
|
|
410
|
+
* Als `ControlValueAccessor` (siehe FieldBase) direkt an Angular-Formulare
|
|
411
|
+
* anbindbar (`[(ngModel)]`, `formControlName`) bzw. ohne Formular per `[(value)]`.
|
|
412
|
+
*/
|
|
413
|
+
declare class TextareaFieldComponent extends FieldBase {
|
|
414
|
+
readonly placeholder: _angular_core.InputSignal<string>;
|
|
415
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TextareaFieldComponent, never>;
|
|
416
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<TextareaFieldComponent, "cds-textarea-field", never, { "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Auswahlfeld — `<select>` im `.field`-Gerüst. Die Optionen kommen über `options`;
|
|
421
|
+
* a11y-Verdrahtung wie beim Textfeld (aria-required/-invalid/-describedby).
|
|
422
|
+
*
|
|
423
|
+
* Als `ControlValueAccessor` (siehe FieldBase) direkt an Angular-Formulare
|
|
424
|
+
* anbindbar (`[(ngModel)]`, `formControlName`) bzw. ohne Formular per `[(value)]`.
|
|
425
|
+
*/
|
|
426
|
+
declare class SelectFieldComponent extends FieldBase {
|
|
427
|
+
/** Auswahloptionen (erste dient üblicherweise als Platzhalter, z. B. „Bitte wählen“). */
|
|
428
|
+
readonly options: _angular_core.InputSignal<string[]>;
|
|
429
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SelectFieldComponent, never>;
|
|
430
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SelectFieldComponent, "cds-select-field", never, { "options": { "alias": "options"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Checkbox — Einwilligungs-/Consent-Feld nach docs/index.html („Forms“).
|
|
435
|
+
*
|
|
436
|
+
* Bewusst KEIN nachgebautes Kästchen: ein natives `<input type="checkbox">` mit
|
|
437
|
+
* `accent-color` in der Bereichsfarbe (genau wie die Doku), umschlossen von einem
|
|
438
|
+
* `<label>` mit dem Einwilligungstext und optional verlinktem Datenschutzhinweis.
|
|
439
|
+
* Dokumentierte Verwendung: Newsletter-/DSGVO-Einwilligung, i. d. R. `required`.
|
|
440
|
+
*
|
|
441
|
+
* Als `ControlValueAccessor` direkt an Angular-Formulare anbindbar (`[(ngModel)]`,
|
|
442
|
+
* `formControlName`); ohne Formular geht `[(checked)]` (checkedChange via model()).
|
|
443
|
+
*/
|
|
444
|
+
declare class CheckboxComponent extends CvaBase<boolean> {
|
|
445
|
+
/** Einwilligungstext neben der Checkbox. */
|
|
446
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
447
|
+
/** Optionaler verlinkter Hinweis am Ende des Labels (z. B. „Datenschutzhinweise“). */
|
|
448
|
+
readonly linkLabel: _angular_core.InputSignal<string>;
|
|
449
|
+
/** Ziel des verlinkten Hinweises. */
|
|
450
|
+
readonly linkHref: _angular_core.InputSignal<string>;
|
|
451
|
+
/** Pflicht-Einwilligung → required + aria-required + .req-Sternchen am Label-Ende. */
|
|
452
|
+
readonly required: _angular_core.InputSignal<boolean>;
|
|
453
|
+
/** Gesetzt/aktiv → checked. Two-Way (`[(checked)]`) UND Angular-Forms. */
|
|
454
|
+
readonly checked: _angular_core.ModelSignal<boolean>;
|
|
455
|
+
/** Deaktiviert; auch über Angular-Forms (setDisabledState) steuerbar. */
|
|
456
|
+
readonly disabled: _angular_core.ModelSignal<boolean>;
|
|
457
|
+
/** Brand Area → accent-color der Checkbox. */
|
|
458
|
+
readonly area: _angular_core.InputSignal<CdsArea>;
|
|
459
|
+
/** @internal */
|
|
460
|
+
protected normalizeValue(value: boolean): boolean;
|
|
461
|
+
/** @internal */
|
|
462
|
+
protected applyValue(value: boolean): void;
|
|
463
|
+
/** @internal */
|
|
464
|
+
protected applyDisabled(disabled: boolean): void;
|
|
465
|
+
/** @internal */
|
|
466
|
+
protected onCheckboxChange(event: Event): void;
|
|
467
|
+
/** @internal */
|
|
468
|
+
protected markTouched(): void;
|
|
469
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<CheckboxComponent, never>;
|
|
470
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<CheckboxComponent, "cds-checkbox", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "linkLabel": { "alias": "linkLabel"; "required": false; "isSignal": true; }; "linkHref": { "alias": "linkHref"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "checked": { "alias": "checked"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; }, { "checked": "checkedChange"; "disabled": "disabledChange"; }, never, never, true, never>;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Radio-Gruppe (Optionsfelder) nach docs/index.html („Forms“).
|
|
475
|
+
*
|
|
476
|
+
* Für 2–6 sich gegenseitig ausschließende Optionen (mehr → Select). Bewusst KEIN
|
|
477
|
+
* nachgebauter Kreis: native `<input type="radio">` mit `accent-color` in der
|
|
478
|
+
* Bereichsfarbe, gruppiert in `<fieldset>`/`<legend>` (Gruppen-Label wie ein
|
|
479
|
+
* Feld-Label), alle Optionen teilen denselben `name`.
|
|
480
|
+
*
|
|
481
|
+
* Als `ControlValueAccessor` direkt an Angular-Formulare anbindbar (`[(ngModel)]`,
|
|
482
|
+
* `formControlName`); ohne Formular geht `[(value)]` (valueChange via model()).
|
|
483
|
+
*/
|
|
484
|
+
declare class RadioGroupComponent extends CvaBase<string> {
|
|
485
|
+
/** Gruppen-Label (die Frage) → <legend>. */
|
|
486
|
+
readonly legend: _angular_core.InputSignal<string>;
|
|
487
|
+
/** Auswahloptionen (Label = Wert). */
|
|
488
|
+
readonly options: _angular_core.InputSignal<string[]>;
|
|
489
|
+
/** Aktuell gewählter Wert. Two-Way (`[(value)]`) UND Angular-Forms. */
|
|
490
|
+
readonly value: _angular_core.ModelSignal<string>;
|
|
491
|
+
/** Gemeinsamer name aller Radios der Gruppe. Default eindeutig. */
|
|
492
|
+
readonly name: _angular_core.InputSignal<string>;
|
|
493
|
+
/** Pflichtauswahl → required + .req-Asterisk. */
|
|
494
|
+
readonly required: _angular_core.InputSignal<boolean>;
|
|
495
|
+
/** Deaktiviert; auch über Angular-Forms (setDisabledState) steuerbar. */
|
|
496
|
+
readonly disabled: _angular_core.ModelSignal<boolean>;
|
|
497
|
+
/** Brand Area → accent-color der Radios. */
|
|
498
|
+
readonly area: _angular_core.InputSignal<CdsArea>;
|
|
499
|
+
/** @internal */
|
|
500
|
+
protected normalizeValue(value: string): string;
|
|
501
|
+
/** @internal */
|
|
502
|
+
protected applyValue(value: string): void;
|
|
503
|
+
/** @internal */
|
|
504
|
+
protected applyDisabled(disabled: boolean): void;
|
|
505
|
+
/** @internal */
|
|
506
|
+
protected onRadioChange(opt: string): void;
|
|
507
|
+
/** @internal */
|
|
508
|
+
protected markTouched(): void;
|
|
509
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<RadioGroupComponent, never>;
|
|
510
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<RadioGroupComponent, "cds-radio-group", never, { "legend": { "alias": "legend"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; }, never, never, true, never>;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
interface CdsSelectOption {
|
|
514
|
+
value: string;
|
|
515
|
+
label: string;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Custom Select (cds-select) — gestylte Einzelauswahl über `.ep-select` aus
|
|
519
|
+
* css/components.css → Doku „Dropdowns“.
|
|
520
|
+
*
|
|
521
|
+
* Trigger-Button + Listbox-Popup mit Häkchen und Bereichs-Akzent (data-area).
|
|
522
|
+
* Anders als die dünnen Wrapper trägt diese Komponente das Verhalten selbst — das
|
|
523
|
+
* ist ihr Zweck (das native <select> reicht dafür nicht): volle Tastatur (↑↓,
|
|
524
|
+
* Pos1/Ende, Type-ahead, Enter wählt, Esc schließt) und die dokumentierte a11y
|
|
525
|
+
* (role=listbox/option, aria-haspopup, aria-expanded, aria-activedescendant,
|
|
526
|
+
* aria-selected). Für kurze Listen in Formularen bleibt das native cds-select-field
|
|
527
|
+
* der Standard; dies hier ist für bereichs-akzentuierte Auswahl.
|
|
528
|
+
*
|
|
529
|
+
* Als `ControlValueAccessor` direkt an Angular-Formulare anbindbar (`[(ngModel)]`,
|
|
530
|
+
* `formControlName`); ohne Formular geht `[(value)]` (valueChange via model()).
|
|
531
|
+
*/
|
|
532
|
+
declare class SelectComponent implements ControlValueAccessor {
|
|
533
|
+
private readonly host;
|
|
534
|
+
private readonly trigger;
|
|
535
|
+
private readonly menu;
|
|
536
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
537
|
+
readonly options: _angular_core.InputSignal<CdsSelectOption[]>;
|
|
538
|
+
/** Aktuell gewählter Wert (value der Option). Two-Way (`[(value)]`) UND Angular-Forms. */
|
|
539
|
+
readonly value: _angular_core.ModelSignal<string | undefined>;
|
|
540
|
+
readonly area: _angular_core.InputSignal<CdsArea | undefined>;
|
|
541
|
+
readonly placeholder: _angular_core.InputSignal<string>;
|
|
542
|
+
/** Deaktiviert; auch über Angular-Forms (setDisabledState) steuerbar. */
|
|
543
|
+
readonly disabled: _angular_core.ModelSignal<boolean>;
|
|
544
|
+
/** Optionaler Feldname → verstecktes Input für den Formular-Submit. */
|
|
545
|
+
readonly name: _angular_core.InputSignal<string | undefined>;
|
|
546
|
+
/** @internal */
|
|
547
|
+
protected readonly open: _angular_core.WritableSignal<boolean>;
|
|
548
|
+
/** @internal */
|
|
549
|
+
protected readonly activeIndex: _angular_core.WritableSignal<number>;
|
|
550
|
+
private onChange;
|
|
551
|
+
private onTouched;
|
|
552
|
+
/** @internal */
|
|
553
|
+
writeValue(value: string | null): void;
|
|
554
|
+
/** @internal */
|
|
555
|
+
registerOnChange(fn: (value: string) => void): void;
|
|
556
|
+
/** @internal */
|
|
557
|
+
registerOnTouched(fn: () => void): void;
|
|
558
|
+
/** @internal */
|
|
559
|
+
setDisabledState(isDisabled: boolean): void;
|
|
560
|
+
private readonly instance;
|
|
561
|
+
/** @internal */
|
|
562
|
+
protected readonly ids: {
|
|
563
|
+
label: string;
|
|
564
|
+
value: string;
|
|
565
|
+
menu: string;
|
|
566
|
+
option: (i: number) => string;
|
|
567
|
+
};
|
|
568
|
+
private typeBuffer;
|
|
569
|
+
private readonly menuFocusTimer;
|
|
570
|
+
private readonly scrollTimer;
|
|
571
|
+
private readonly typeaheadTimer;
|
|
572
|
+
/** @internal */
|
|
573
|
+
protected selectedOption(): CdsSelectOption | undefined;
|
|
574
|
+
/** @internal */
|
|
575
|
+
protected toggle(): void;
|
|
576
|
+
private openMenu;
|
|
577
|
+
/** @internal */
|
|
578
|
+
protected close(focusTrigger?: boolean): void;
|
|
579
|
+
/** @internal */
|
|
580
|
+
protected select(i: number): void;
|
|
581
|
+
/** @internal */
|
|
582
|
+
protected markTouched(): void;
|
|
583
|
+
/**
|
|
584
|
+
* `onTouched` erst, wenn der Fokus die GESAMTE Komponente verlässt — nicht schon beim
|
|
585
|
+
* Öffnen, wenn er vom Trigger in die Listbox wandert (beides liegt im Host). Sonst
|
|
586
|
+
* wäre das Control „touched“, bevor überhaupt ausgewählt wurde.
|
|
587
|
+
*
|
|
588
|
+
* @internal
|
|
589
|
+
*/
|
|
590
|
+
protected onFocusOut(event: FocusEvent): void;
|
|
591
|
+
/**
|
|
592
|
+
* Tastatur am Trigger-Button: nur Öffnen (im offenen Zustand hat die Listbox Fokus).
|
|
593
|
+
*
|
|
594
|
+
* @internal
|
|
595
|
+
*/
|
|
596
|
+
protected onTriggerKeydown(event: KeyboardEvent): void;
|
|
597
|
+
/**
|
|
598
|
+
* Tastatur in der offenen Listbox: Navigation, Auswahl, Schließen, Type-ahead.
|
|
599
|
+
*
|
|
600
|
+
* @internal
|
|
601
|
+
*/
|
|
602
|
+
protected onMenuKeydown(event: KeyboardEvent): void;
|
|
603
|
+
private move;
|
|
604
|
+
private setActive;
|
|
605
|
+
private typeahead;
|
|
606
|
+
/** @internal */
|
|
607
|
+
protected onDocPointerDown(event: PointerEvent): void;
|
|
608
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SelectComponent, never>;
|
|
609
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SelectComponent, "cds-select", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; }, never, never, true, never>;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Combobox (cds-combobox) — Tipp-Filter über `.ep-combobox` aus css/components.css
|
|
614
|
+
* → Doku „Dropdowns“. Wie der Custom Select, aber mit Substring-Filter im Feld (für
|
|
615
|
+
* lange Listen). Optional Multi-Select mit Chips (`multi`).
|
|
616
|
+
*
|
|
617
|
+
* Verhalten/a11y selbst getragen: Input mit role=combobox (dort ist
|
|
618
|
+
* aria-activedescendant erlaubt), aria-expanded/-controls/-autocomplete; Listbox mit
|
|
619
|
+
* role=listbox/option; ↓ öffnet/navigiert, Enter wählt, Esc schließt, Rücktaste bei
|
|
620
|
+
* leerem Feld entfernt im Multi-Modus den letzten Chip. Beim Schließen ohne Auswahl
|
|
621
|
+
* fällt die Einzelauswahl auf das gewählte Label zurück (kein loser Filtertext).
|
|
622
|
+
*
|
|
623
|
+
* Als `ControlValueAccessor` direkt an Angular-Formulare anbindbar (`[(ngModel)]`,
|
|
624
|
+
* `formControlName`); der Formularwert ist im Multi-Modus `string[]`, sonst `string`.
|
|
625
|
+
* Ohne Formular gehen `[(value)]` / `[(values)]` (valueChange/valuesChange via model()).
|
|
626
|
+
*/
|
|
627
|
+
declare class ComboboxComponent implements ControlValueAccessor {
|
|
628
|
+
private readonly host;
|
|
629
|
+
private readonly input;
|
|
630
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
631
|
+
readonly options: _angular_core.InputSignal<CdsSelectOption[]>;
|
|
632
|
+
/** Einzelauswahl: gewählter Wert. Two-Way (`[(value)]`) UND Angular-Forms. */
|
|
633
|
+
readonly value: _angular_core.ModelSignal<string | undefined>;
|
|
634
|
+
/** Mehrfachauswahl: gewählte Werte (nur bei multi). Two-Way UND Angular-Forms. */
|
|
635
|
+
readonly values: _angular_core.ModelSignal<string[]>;
|
|
636
|
+
readonly multi: _angular_core.InputSignal<boolean>;
|
|
637
|
+
readonly area: _angular_core.InputSignal<CdsArea | undefined>;
|
|
638
|
+
readonly placeholder: _angular_core.InputSignal<string>;
|
|
639
|
+
readonly emptyText: _angular_core.InputSignal<string>;
|
|
640
|
+
/** Deaktiviert; auch über Angular-Forms (setDisabledState) steuerbar. */
|
|
641
|
+
readonly disabled: _angular_core.ModelSignal<boolean>;
|
|
642
|
+
/** @internal */
|
|
643
|
+
protected readonly open: _angular_core.WritableSignal<boolean>;
|
|
644
|
+
/** @internal */
|
|
645
|
+
protected readonly activeIndex: _angular_core.WritableSignal<number>;
|
|
646
|
+
/**
|
|
647
|
+
* Aktuell gewählte Werte — reiner Ableitungszustand aus `[(value)]`/`[(values)]`,
|
|
648
|
+
* je nach Modus (WP5 §5.3: vorher ein per `effect()` nachgeführtes Signal; die
|
|
649
|
+
* `effect()`-Zustandsableitung ist das Anti-Muster, das die Angular-Doku
|
|
650
|
+
* ausdrücklich nennt). Alle Schreibpfade (`select()`, `removeValue()`,
|
|
651
|
+
* `writeValue()`) schreiben seitdem direkt in `value`/`values` — die Quelle,
|
|
652
|
+
* nicht mehr in dieses abgeleitete Signal.
|
|
653
|
+
*
|
|
654
|
+
* @internal
|
|
655
|
+
*/
|
|
656
|
+
protected readonly selected: _angular_core.Signal<string[]>;
|
|
657
|
+
/** Label der aktuellen Einzelauswahl (leer bei Multi/keiner Auswahl). */
|
|
658
|
+
private readonly selectedLabel;
|
|
659
|
+
/**
|
|
660
|
+
* Sichtbarer Filtertext im Feld. Vorgabe: leer im Multi-Modus, sonst das Label
|
|
661
|
+
* der aktuellen Auswahl — lokal überschreibbar (Tippen via `onInput`,
|
|
662
|
+
* `select()`/`clear()`/`close()` setzen ihn ebenfalls direkt). Aktualisiert sich
|
|
663
|
+
* NICHT von der Vorgabe her, während das Menü offen ist (Nutzer tippt gerade);
|
|
664
|
+
* erst das nächste Schließen übernimmt die neue Vorgabe. Ersetzt den Teil des
|
|
665
|
+
* bisherigen `effect()`, der `query` außerhalb des offenen Menüs nachführte
|
|
666
|
+
* (WP5 §5.3).
|
|
667
|
+
*
|
|
668
|
+
* @internal
|
|
669
|
+
*/
|
|
670
|
+
protected readonly query: _angular_core.WritableSignal<string>;
|
|
671
|
+
private onChange;
|
|
672
|
+
private onTouched;
|
|
673
|
+
private readonly instance;
|
|
674
|
+
/** @internal */
|
|
675
|
+
protected readonly ids: {
|
|
676
|
+
label: string;
|
|
677
|
+
menu: string;
|
|
678
|
+
option: (i: number) => string;
|
|
679
|
+
};
|
|
680
|
+
/**
|
|
681
|
+
* ControlValueAccessor — Formularwert ist string[] (multi) bzw. string (single).
|
|
682
|
+
*
|
|
683
|
+
* @internal
|
|
684
|
+
*/
|
|
685
|
+
writeValue(value: string | string[] | null): void;
|
|
686
|
+
/** @internal */
|
|
687
|
+
registerOnChange(fn: (value: string | string[]) => void): void;
|
|
688
|
+
/** @internal */
|
|
689
|
+
registerOnTouched(fn: () => void): void;
|
|
690
|
+
/** @internal */
|
|
691
|
+
setDisabledState(isDisabled: boolean): void;
|
|
692
|
+
/**
|
|
693
|
+
* Aktuell gewählte Optionen (für die Chips im Multi-Modus).
|
|
694
|
+
*
|
|
695
|
+
* @internal
|
|
696
|
+
*/
|
|
697
|
+
protected selectedOptions(): CdsSelectOption[];
|
|
698
|
+
/**
|
|
699
|
+
* Gefilterte Optionen: Substring (case-insensitiv); im Multi-Modus ohne bereits Gewählte.
|
|
700
|
+
*
|
|
701
|
+
* @internal
|
|
702
|
+
*/
|
|
703
|
+
protected filtered(): CdsSelectOption[];
|
|
704
|
+
/** @internal */
|
|
705
|
+
protected isSelected(value: string): boolean;
|
|
706
|
+
/** @internal */
|
|
707
|
+
protected focusInput(): void;
|
|
708
|
+
/** @internal */
|
|
709
|
+
protected openMenu(): void;
|
|
710
|
+
/** @internal */
|
|
711
|
+
protected close(): void;
|
|
712
|
+
/** @internal */
|
|
713
|
+
protected onInput(event: Event): void;
|
|
714
|
+
/** @internal */
|
|
715
|
+
protected select(opt: CdsSelectOption): void;
|
|
716
|
+
/** @internal */
|
|
717
|
+
protected removeValue(value: string, event?: Event): void;
|
|
718
|
+
/** @internal */
|
|
719
|
+
protected clear(event?: Event): void;
|
|
720
|
+
/** @internal */
|
|
721
|
+
protected markTouched(): void;
|
|
722
|
+
/** @internal */
|
|
723
|
+
protected onKeydown(event: KeyboardEvent): void;
|
|
724
|
+
private readonly scrollTimer;
|
|
725
|
+
private scrollActive;
|
|
726
|
+
/** @internal */
|
|
727
|
+
protected onDocPointerDown(event: PointerEvent): void;
|
|
728
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ComboboxComponent, never>;
|
|
729
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ComboboxComponent, "cds-combobox", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "values": { "alias": "values"; "required": false; "isSignal": true; }; "multi": { "alias": "multi"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "emptyText": { "alias": "emptyText"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "values": "valuesChange"; "disabled": "disabledChange"; }, never, never, true, never>;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* Slider — Wrapper um `.field-slider` / `.slider` aus css/components.css → „Slider“.
|
|
734
|
+
*
|
|
735
|
+
* Range-Eingabe mit Label + Live-Ausgabe, bereichsgefärbtem Thumb/Output
|
|
736
|
+
* (.slider-<area> setzt --sl-color), automatisch berechneten Ticks und Helper-Text.
|
|
737
|
+
*
|
|
738
|
+
* Ticks: Es wird nur eine gewünschte ANZAHL (`tickCount`) angegeben; die Positionen/
|
|
739
|
+
* Labels werden gleichmäßig aus [min, max] berechnet (space-between wie das CSS-
|
|
740
|
+
* Layout). Wird der Slider zu schmal, reduziert die Komponente die Tick-Zahl reaktiv
|
|
741
|
+
* (ResizeObserver), sodass die Labels nicht überlappen — Endpunkte bleiben erhalten.
|
|
742
|
+
*
|
|
743
|
+
* Als `ControlValueAccessor` direkt an Angular-Formulare anbindbar (`[(ngModel)]`,
|
|
744
|
+
* `formControlName`); ohne Formular geht `[(value)]` (valueChange via model()).
|
|
745
|
+
*/
|
|
746
|
+
declare class SliderComponent extends CvaBase<number> {
|
|
747
|
+
private readonly host;
|
|
748
|
+
private readonly destroyRef;
|
|
749
|
+
/** Gemessene Slider-Breite (px); treibt die reaktive Tick-Reduktion. */
|
|
750
|
+
private readonly width;
|
|
751
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
752
|
+
/** Markenbereich → .slider-<area> (Thumb- + Output-Farbe). */
|
|
753
|
+
readonly area: _angular_core.InputSignal<CdsArea>;
|
|
754
|
+
readonly min: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
755
|
+
readonly max: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
756
|
+
readonly step: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
757
|
+
/** Aktueller Wert. Two-Way (`[(value)]`) UND Angular-Forms. */
|
|
758
|
+
readonly value: _angular_core.ModelSignal<number>;
|
|
759
|
+
/** Einheit, an den formatierten Wert angehängt (z. B. ' €'). */
|
|
760
|
+
readonly unit: _angular_core.InputSignal<string>;
|
|
761
|
+
/** Gewünschte Anzahl Ticks (inkl. Endpunkte). 0 = keine. Wird bei zu schmalem
|
|
762
|
+
* Slider automatisch reduziert. */
|
|
763
|
+
readonly tickCount: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
764
|
+
/** Mindestbreite (px) pro Tick-Label, ab der reduziert wird. */
|
|
765
|
+
readonly minTickSpacing: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
766
|
+
readonly helper: _angular_core.InputSignal<string>;
|
|
767
|
+
/** Deaktiviert; auch über Angular-Forms (setDisabledState) steuerbar. */
|
|
768
|
+
readonly disabled: _angular_core.ModelSignal<boolean>;
|
|
769
|
+
readonly sliderId: _angular_core.InputSignal<string>;
|
|
770
|
+
constructor();
|
|
771
|
+
/** @internal */
|
|
772
|
+
protected normalizeValue(value: number): number;
|
|
773
|
+
private clamp;
|
|
774
|
+
/** @internal */
|
|
775
|
+
protected applyValue(value: number): void;
|
|
776
|
+
/** @internal */
|
|
777
|
+
protected applyDisabled(disabled: boolean): void;
|
|
778
|
+
/** @internal */
|
|
779
|
+
protected readonly sliderClasses: _angular_core.Signal<string>;
|
|
780
|
+
/** @internal */
|
|
781
|
+
protected readonly outputClasses: _angular_core.Signal<string>;
|
|
782
|
+
/** @internal */
|
|
783
|
+
protected readonly formatted: _angular_core.Signal<string>;
|
|
784
|
+
/** Effektive Tick-Anzahl: Wunsch, aber auf das reduziert, was in die Breite passt. */
|
|
785
|
+
private effectiveTickCount;
|
|
786
|
+
/**
|
|
787
|
+
* Gleichmäßig über [min, max] verteilte Ticks: Label (kompakt formatiert) plus
|
|
788
|
+
* absolute Ziel-Position (left), zentriert auf die echte Thumb-Position.
|
|
789
|
+
*
|
|
790
|
+
* @internal
|
|
791
|
+
*/
|
|
792
|
+
protected readonly tickItems: _angular_core.Signal<{
|
|
793
|
+
label: string;
|
|
794
|
+
left: string;
|
|
795
|
+
}[]>;
|
|
796
|
+
/** Kompakte Tick-Beschriftung: k/M-Kurzform (de-DE), z. B. 32500 → „32,5k“. */
|
|
797
|
+
private formatTick;
|
|
798
|
+
/** @internal */
|
|
799
|
+
protected onInput(event: Event): void;
|
|
800
|
+
/** @internal */
|
|
801
|
+
protected markTouched(): void;
|
|
802
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SliderComponent, never>;
|
|
803
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SliderComponent, "cds-slider", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; "min": { "alias": "min"; "required": false; "isSignal": true; }; "max": { "alias": "max"; "required": false; "isSignal": true; }; "step": { "alias": "step"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "unit": { "alias": "unit"; "required": false; "isSignal": true; }; "tickCount": { "alias": "tickCount"; "required": false; "isSignal": true; }; "minTickSpacing": { "alias": "minTickSpacing"; "required": false; "isSignal": true; }; "helper": { "alias": "helper"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "sliderId": { "alias": "sliderId"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; }, never, never, true, never>;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
interface CdsFooterLink {
|
|
807
|
+
label: string;
|
|
808
|
+
href: string;
|
|
809
|
+
}
|
|
810
|
+
/** Built-in-Plattformen mit verifiziertem Icon. Weitere über `iconPath` möglich. */
|
|
811
|
+
type CdsSocialPlatform = 'linkedin' | 'youtube';
|
|
812
|
+
interface CdsSocialLink {
|
|
813
|
+
href: string;
|
|
814
|
+
/** Built-in-Plattform (bringt Icon + Label mit). */
|
|
815
|
+
platform?: CdsSocialPlatform;
|
|
816
|
+
/** Aria-Label; Default: das Label der Plattform. */
|
|
817
|
+
label?: string;
|
|
818
|
+
/** Eigenes SVG-Pfad-`d` (viewBox 0 0 24, weiß) — für Plattformen ohne Built-in. */
|
|
819
|
+
iconPath?: string;
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Footer — unterer Teil (`.footer-btm`): dunkler Streifen mit Copyright, Rechts-Links
|
|
823
|
+
* und Social-Profilen. Die Host-Klasse `footer-btm` trägt Hintergrund/Flex-Layout aus
|
|
824
|
+
* css/components.css. Social-Glyphen sind schlanke Inline-SVGs in Weiß; verifizierte
|
|
825
|
+
* Built-in-Icons (linkedin, youtube) via `platform`, beliebige weitere via `iconPath`.
|
|
826
|
+
*/
|
|
827
|
+
declare class FooterBottomComponent {
|
|
828
|
+
readonly copyright: _angular_core.InputSignal<string>;
|
|
829
|
+
/** Optionale Versionsangabe (z. B. „Version 1.4.2“), hinter dem Copyright. */
|
|
830
|
+
readonly version: _angular_core.InputSignal<string | undefined>;
|
|
831
|
+
/** Optionaler Support-/Hilfe-Link neben den Rechts-Links. */
|
|
832
|
+
readonly support: _angular_core.InputSignal<CdsFooterLink | undefined>;
|
|
833
|
+
readonly legalLinks: _angular_core.InputSignal<CdsFooterLink[]>;
|
|
834
|
+
readonly socialLinks: _angular_core.InputSignal<CdsSocialLink[]>;
|
|
835
|
+
/** Verifizierte Built-in-Social-Icons (viewBox 0 0 24, weiß). */
|
|
836
|
+
private readonly socialIcons;
|
|
837
|
+
/** @internal */
|
|
838
|
+
protected socialPath(s: CdsSocialLink): string;
|
|
839
|
+
/** @internal */
|
|
840
|
+
protected socialLabel(s: CdsSocialLink): string;
|
|
841
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FooterBottomComponent, never>;
|
|
842
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FooterBottomComponent, "cds-footer-bottom", never, { "copyright": { "alias": "copyright"; "required": false; "isSignal": true; }; "version": { "alias": "version"; "required": false; "isSignal": true; }; "support": { "alias": "support"; "required": false; "isSignal": true; }; "legalLinks": { "alias": "legalLinks"; "required": false; "isSignal": true; }; "socialLinks": { "alias": "socialLinks"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Footer — `<footer>`-Landmark (`.footer`, Zwei-Band-Stack) als schlanke Hülle. Die
|
|
847
|
+
* beiden Bänder werden als `<cds-footer-main>` und `<cds-footer-bottom>` projiziert:
|
|
848
|
+
*
|
|
849
|
+
* ```html
|
|
850
|
+
* <cds-footer>
|
|
851
|
+
* <cds-footer-main ...></cds-footer-main>
|
|
852
|
+
* <cds-footer-bottom ...></cds-footer-bottom>
|
|
853
|
+
* </cds-footer>
|
|
854
|
+
* ```
|
|
855
|
+
*
|
|
856
|
+
* So lassen sich oberer und unterer Teil einzeln betrachten und unabhängig
|
|
857
|
+
* konfigurieren; `.footer` stapelt sie (flex column) im Seitenfuß-Landmark.
|
|
858
|
+
*/
|
|
859
|
+
declare class FooterComponent {
|
|
860
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FooterComponent, never>;
|
|
861
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FooterComponent, "cds-footer", never, {}, {}, never, ["*"], true, never>;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* Footer — oberer Teil (`.footer-main`): helles Band als **generisches Spalten-Layout**.
|
|
866
|
+
* Der Inhalt wird als beliebige Spalten projiziert (jedes Top-Level-Kind = eine
|
|
867
|
+
* Grid-Spalte), statt fester Adresse/Nav/Newsletter-Struktur — so eignet es sich für
|
|
868
|
+
* Website- UND App-Footer ohne Marketing-Lock-in.
|
|
869
|
+
*
|
|
870
|
+
* ```html
|
|
871
|
+
* <cds-footer-main>
|
|
872
|
+
* <div>…Spalte 1…</div>
|
|
873
|
+
* <div>…Spalte 2…</div>
|
|
874
|
+
* </cds-footer-main>
|
|
875
|
+
* ```
|
|
876
|
+
*
|
|
877
|
+
* Die Host-Klasse `footer-main` trägt Hintergrund/Padding aus css/components.css;
|
|
878
|
+
* `:host{display:block}` macht das Band auch standalone zum Block. Spaltenanzahl/-breiten
|
|
879
|
+
* über `columns` (grid-template-columns); ohne Angabe gilt das 3-Spalten-Default.
|
|
880
|
+
*/
|
|
881
|
+
declare class FooterMainComponent {
|
|
882
|
+
/**
|
|
883
|
+
* Optionale `grid-template-columns` (z. B. `'repeat(4, 1fr)'` oder `'2fr 1fr'`).
|
|
884
|
+
* Ohne Angabe gilt das Default aus `.footer-grid` (1.2fr 1fr 1.3fr, 3 Spalten).
|
|
885
|
+
*/
|
|
886
|
+
readonly columns: _angular_core.InputSignal<string | undefined>;
|
|
887
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FooterMainComponent, never>;
|
|
888
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FooterMainComponent, "cds-footer-main", never, { "columns": { "alias": "columns"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* Einzelner Tab für `<cds-area-tabs>`. Trägt Label + Bereich; sein Inhalt ist
|
|
893
|
+
* BELIEBIGES projiziertes Markup (Text, Listen, Komponenten, CTAs …), das die
|
|
894
|
+
* Elternkomponente ins zugehörige Panel rendert.
|
|
895
|
+
*
|
|
896
|
+
* Der Inhalt wird in ein internes `<ng-template>` projiziert (via `<ng-content>`)
|
|
897
|
+
* und über einen TemplateRef bereitgestellt — so kann `cds-area-tabs` ihn getrennt
|
|
898
|
+
* vom Tab-Button an der Panel-Position ausgeben (ngTemplateOutlet), passend zur
|
|
899
|
+
* `.area-tabs` / `.atab-content`-Struktur aus css/components.css.
|
|
900
|
+
*/
|
|
901
|
+
declare class AreaTabComponent {
|
|
902
|
+
/** Markenbereich → Tab-Punkt (Bereichs-500) + aktiver Text/Unterstrich. */
|
|
903
|
+
readonly area: _angular_core.InputSignal<CdsArea>;
|
|
904
|
+
/** Beschriftung im Tab-Button. */
|
|
905
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
906
|
+
/**
|
|
907
|
+
* Projizierter Panel-Inhalt; von der Elternkomponente via Outlet gerendert.
|
|
908
|
+
*
|
|
909
|
+
* Bleibt `public`, weil `cds-area-tabs` ihn von außen liest — anders als die
|
|
910
|
+
* internen Signale, die ADR-0007 auf `protected` stellt. `@internal` hält ihn
|
|
911
|
+
* trotzdem aus Props-Tabelle und Manifest heraus: Input/Output ist er nicht.
|
|
912
|
+
*
|
|
913
|
+
* @internal
|
|
914
|
+
*/
|
|
915
|
+
readonly content: _angular_core.Signal<TemplateRef<unknown>>;
|
|
916
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<AreaTabComponent, never>;
|
|
917
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<AreaTabComponent, "cds-area-tab", never, { "area": { "alias": "area"; "required": true; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* AreaTabs — Wrapper um `.area-tabs` / `.atab` / `.atab-content` aus
|
|
922
|
+
* css/components.css → „Area Tabs“. Bereichsgefärbte Tab-Leiste: der aktive Tab
|
|
923
|
+
* setzt `--atab-color` (Bereichs-700) für Text + Unterstrich, der Punkt nutzt
|
|
924
|
+
* Bereichs-500. Umschalten per Klick (aria-selected / sichtbares Panel).
|
|
925
|
+
*
|
|
926
|
+
* Tabs werden deklarativ als `<cds-area-tab>`-Kinder projiziert; jeder Tab trägt
|
|
927
|
+
* `area` + `label` und BELIEBIGEN Inhalt (Markup/Komponenten), der ins Panel
|
|
928
|
+
* gerendert wird. Der aktive Index ist über `[(active)]` steuerbar.
|
|
929
|
+
*/
|
|
930
|
+
declare class AreaTabsComponent {
|
|
931
|
+
private readonly host;
|
|
932
|
+
private readonly instance;
|
|
933
|
+
/**
|
|
934
|
+
* Die Tabs kommen als projizierte `<cds-area-tab>`-Kinder.
|
|
935
|
+
*
|
|
936
|
+
* @internal
|
|
937
|
+
*/
|
|
938
|
+
protected readonly tabs: _angular_core.Signal<readonly AreaTabComponent[]>;
|
|
939
|
+
/** Index des aktiven Tabs. Two-Way (`[(active)]`). */
|
|
940
|
+
readonly active: _angular_core.ModelSignal<number>;
|
|
941
|
+
/** Zugänglicher Name der Tab-Leiste (WAI-ARIA verlangt aria-label/-labelledby). */
|
|
942
|
+
readonly ariaLabel: _angular_core.InputSignal<string>;
|
|
943
|
+
/**
|
|
944
|
+
* Aktiv-Akzent je Bereich: ki braucht -800 (700 reißt AA), sonst -700 (wie .t-*).
|
|
945
|
+
*
|
|
946
|
+
* @internal
|
|
947
|
+
*/
|
|
948
|
+
protected atabAccent(area: CdsArea): string;
|
|
949
|
+
/** @internal */
|
|
950
|
+
protected tabId(i: number): string;
|
|
951
|
+
/** @internal */
|
|
952
|
+
protected panelId(i: number): string;
|
|
953
|
+
/**
|
|
954
|
+
* WAI-ARIA-Tabs-Tastatur (horizontal, automatische Aktivierung): ←/→ bewegen mit
|
|
955
|
+
* Umlauf, Home/End springen an die Enden. Der Fokus wird mitgeführt (Roving
|
|
956
|
+
* Tabindex: nur der aktive Tab ist per Tab erreichbar). Panels laden sofort, daher
|
|
957
|
+
* ist Auswahl = Fokus (APG-empfohlen).
|
|
958
|
+
*
|
|
959
|
+
* @internal
|
|
960
|
+
*/
|
|
961
|
+
protected onKeydown(event: KeyboardEvent): void;
|
|
962
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<AreaTabsComponent, never>;
|
|
963
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<AreaTabsComponent, "cds-area-tabs", never, { "active": { "alias": "active"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, { "active": "activeChange"; }, ["tabs"], never, true, never>;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
/** Ein Logo: in der Regel ein Bild; der Text dient als Alt-Fallback und als Platzhalter. */
|
|
967
|
+
interface CdsLogo {
|
|
968
|
+
/** Firmen-/Markenname. Pflicht – dient als Alt-Text-Fallback UND als Text-Platzhalter,
|
|
969
|
+
* falls (noch) kein Bild vorliegt. */
|
|
970
|
+
label: string;
|
|
971
|
+
/** Bildquelle (URL oder Data-URI). Gesetzt → Bild wird gezeigt, sonst der Text-Platzhalter. */
|
|
972
|
+
src?: string;
|
|
973
|
+
/** Alt-Text des Bilds (Fallback: `label`). */
|
|
974
|
+
alt?: string;
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* Logo — einzelne Logo-Kachel (`.logo-tile` aus css/components.css).
|
|
978
|
+
*
|
|
979
|
+
* Rendert bevorzugt ein **Bild** (`src`); ohne Bild fällt es auf den Text-`label`
|
|
980
|
+
* als Platzhalter zurück (`.logo-placeholder`). So lässt es sich sowohl im
|
|
981
|
+
* Logo-Carousel als auch eigenständig verwenden (Partner-Leiste, „Bekannt aus“-Reihe,
|
|
982
|
+
* Footer). Die Host-Klasse `logo-tile` trägt Flex-Zentrierung/Padding aus der portablen CSS.
|
|
983
|
+
*/
|
|
984
|
+
declare class LogoComponent {
|
|
985
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
986
|
+
/** Bildquelle; gesetzt → Bild statt Text-Platzhalter. */
|
|
987
|
+
readonly src: _angular_core.InputSignal<string | undefined>;
|
|
988
|
+
/** Alt-Text des Bilds (Fallback: `label`). */
|
|
989
|
+
readonly alt: _angular_core.InputSignal<string>;
|
|
990
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<LogoComponent, never>;
|
|
991
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<LogoComponent, "cds-logo", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; "src": { "alias": "src"; "required": false; "isSignal": true; }; "alt": { "alias": "alt"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/**
|
|
995
|
+
* LogoCarousel — Wrapper um `.logo-carousel` aus css/components.css → „Logo-Carousel“.
|
|
996
|
+
*
|
|
997
|
+
* Diskrete Sets von je fünf Logos, die automatisch per Crossfade wechseln
|
|
998
|
+
* (`[aria-hidden]` je Slide). Das Autoplay pausiert bei Maus-Hover und Tastatur-Fokus
|
|
999
|
+
* (damit Nutzer in Ruhe lesen/bedienen können), zusätzlich dauerhaft über den
|
|
1000
|
+
* Pause-Button (.logo-carousel-pause, sichtbar bei Hover/Fokus bzw. im .paused-Zustand);
|
|
1001
|
+
* Dots wählen ein Set direkt. Autoplay respektiert prefers-reduced-motion.
|
|
1002
|
+
*
|
|
1003
|
+
* Der Timer wird zentral über ein `effect` gesteuert: er läuft nur, wenn NICHT
|
|
1004
|
+
* pausiert, NICHT gehovert, NICHT fokussiert und reduzierte Bewegung nicht gewünscht
|
|
1005
|
+
* ist. Eine Änderung des `interval` startet ihn automatisch neu.
|
|
1006
|
+
*
|
|
1007
|
+
* Jede Kachel ist ein `cds-logo`: bevorzugt ein Bild (`src`), sonst der Text als
|
|
1008
|
+
* Platzhalter/Fallback. Standardmäßig sind reine Text-Platzhalter gesetzt – reale
|
|
1009
|
+
* Anwendungen übergeben ihre Kundenlogos als Bilder.
|
|
1010
|
+
*/
|
|
1011
|
+
declare class LogoCarouselComponent {
|
|
1012
|
+
private readonly host;
|
|
1013
|
+
/** Logo-Sets, die im Wechsel angezeigt werden (mind. ein Eintrag je Set). */
|
|
1014
|
+
readonly sets: _angular_core.InputSignal<CdsLogo[][]>;
|
|
1015
|
+
/** Autoplay-Intervall in **Millisekunden** (Standard 6000 = 6 s). */
|
|
1016
|
+
readonly interval: _angular_core.InputSignal<number>;
|
|
1017
|
+
/** Aktives Set. Two-Way (`[(active)]`) via model(). */
|
|
1018
|
+
readonly active: _angular_core.ModelSignal<number>;
|
|
1019
|
+
/**
|
|
1020
|
+
* Vom Nutzer explizit pausiert (Pause-Button).
|
|
1021
|
+
*
|
|
1022
|
+
* @internal
|
|
1023
|
+
*/
|
|
1024
|
+
protected readonly paused: _angular_core.WritableSignal<boolean>;
|
|
1025
|
+
/**
|
|
1026
|
+
* Transiente Pause: Maus über dem Carousel.
|
|
1027
|
+
*
|
|
1028
|
+
* @internal
|
|
1029
|
+
*/
|
|
1030
|
+
protected readonly hovered: _angular_core.WritableSignal<boolean>;
|
|
1031
|
+
/**
|
|
1032
|
+
* Transiente Pause: Tastatur-Fokus innerhalb des Carousels.
|
|
1033
|
+
*
|
|
1034
|
+
* @internal
|
|
1035
|
+
*/
|
|
1036
|
+
protected readonly focused: _angular_core.WritableSignal<boolean>;
|
|
1037
|
+
/** Reduzierte Bewegung gewünscht → kein Autoplay. Einmal beim Erzeugen ermittelt. */
|
|
1038
|
+
private readonly reducedMotion;
|
|
1039
|
+
private readonly uid;
|
|
1040
|
+
constructor();
|
|
1041
|
+
/**
|
|
1042
|
+
* Stabile Slide-id für die aria-controls-Verknüpfung der Dots.
|
|
1043
|
+
*
|
|
1044
|
+
* @internal
|
|
1045
|
+
*/
|
|
1046
|
+
protected slideId(i: number): string;
|
|
1047
|
+
/** @internal */
|
|
1048
|
+
protected togglePause(): void;
|
|
1049
|
+
/** @internal */
|
|
1050
|
+
protected goTo(i: number): void;
|
|
1051
|
+
/**
|
|
1052
|
+
* WAI-ARIA-Tabs-Tastatur auf der Dot-Leiste (horizontal, automatische Aktivierung):
|
|
1053
|
+
* ←/→ mit Umlauf, Home/End an die Enden; der Fokus wird mitgeführt (Roving Tabindex).
|
|
1054
|
+
* 1:1 übernommen von `carousel.component.ts` (Indexberechnung in
|
|
1055
|
+
* `../shared/dots-keyboard.ts`, identisch für beide Komponenten).
|
|
1056
|
+
*
|
|
1057
|
+
* @internal
|
|
1058
|
+
*/
|
|
1059
|
+
protected onDotsKeydown(event: KeyboardEvent): void;
|
|
1060
|
+
/**
|
|
1061
|
+
* Fokus-Pause nur aufheben, wenn der Fokus das Carousel ganz verlässt (nicht bei
|
|
1062
|
+
* Wechsel zwischen Kind-Elementen).
|
|
1063
|
+
*
|
|
1064
|
+
* @internal
|
|
1065
|
+
*/
|
|
1066
|
+
protected onFocusOut(event: FocusEvent): void;
|
|
1067
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<LogoCarouselComponent, never>;
|
|
1068
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<LogoCarouselComponent, "cds-logo-carousel", never, { "sets": { "alias": "sets"; "required": true; "isSignal": true; }; "interval": { "alias": "interval"; "required": false; "isSignal": true; }; "active": { "alias": "active"; "required": false; "isSignal": true; }; }, { "active": "activeChange"; }, never, never, true, never>;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
interface CdsSlide {
|
|
1072
|
+
image?: string;
|
|
1073
|
+
title: string;
|
|
1074
|
+
text: string;
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Carousel — Wrapper um `.img-slider` aus css/components.css → „Bild-Slider“.
|
|
1078
|
+
*
|
|
1079
|
+
* Crossfade-Carousel: alle Slides liegen gestapelt im Grid, die aktive trägt
|
|
1080
|
+
* `.active` (opacity). Inaktive Slides bekommen zusätzlich `[aria-hidden]`, sonst
|
|
1081
|
+
* läse ein Screenreader Titel und Text jeder Folie vor, obwohl visuell nur eine
|
|
1082
|
+
* sichtbar ist (identisches Muster wie beim LogoCarousel). Prev/Next-Buttons
|
|
1083
|
+
* (.img-slider-btn) und Dots (.img-dot) steuern den Index. Optionale Hero-Variante
|
|
1084
|
+
* (.img-slider-hero). Ohne Bild-URL wird ein neutraler Platzhalter gezeigt. Der
|
|
1085
|
+
* aktive Index ist über `[(active)]` (model) beobacht-/steuerbar.
|
|
1086
|
+
*/
|
|
1087
|
+
declare class CarouselComponent {
|
|
1088
|
+
private readonly host;
|
|
1089
|
+
private readonly instance;
|
|
1090
|
+
/** Anzuzeigende Slides (Bild optional, Titel/Text je Slide Pflicht). */
|
|
1091
|
+
readonly slides: _angular_core.InputSignal<CdsSlide[]>;
|
|
1092
|
+
/** Aktiver Slide-Index. Two-Way (`[(active)]`) via model(). */
|
|
1093
|
+
readonly active: _angular_core.ModelSignal<number>;
|
|
1094
|
+
/** Hero-Variante (vollflächig, 21:9, Caption als Overlay) → .img-slider-hero. */
|
|
1095
|
+
readonly hero: _angular_core.InputSignal<boolean>;
|
|
1096
|
+
/** @internal */
|
|
1097
|
+
protected readonly wrapClasses: _angular_core.Signal<"img-slider img-slider-hero" | "img-slider">;
|
|
1098
|
+
/** @internal */
|
|
1099
|
+
protected prev(): void;
|
|
1100
|
+
/** @internal */
|
|
1101
|
+
protected next(): void;
|
|
1102
|
+
/** @internal */
|
|
1103
|
+
protected slideId(i: number): string;
|
|
1104
|
+
/**
|
|
1105
|
+
* WAI-ARIA-Tabs-Tastatur auf der Dot-Leiste (horizontal, automatische Aktivierung):
|
|
1106
|
+
* ←/→ mit Umlauf, Home/End an die Enden; der Fokus wird mitgeführt (Roving Tabindex).
|
|
1107
|
+
* Indexberechnung in `../shared/dots-keyboard.ts` (identisch mit LogoCarousel).
|
|
1108
|
+
*
|
|
1109
|
+
* @internal
|
|
1110
|
+
*/
|
|
1111
|
+
protected onDotsKeydown(event: KeyboardEvent): void;
|
|
1112
|
+
/** @internal */
|
|
1113
|
+
protected readonly placeholder = "data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='800'%20height='450'%3E%3Crect%20width='800'%20height='450'%20fill='%23E8EDED'/%3E%3Ctext%20x='400'%20y='225'%20font-family='sans-serif'%20font-size='24'%20fill='%236E8585'%20text-anchor='middle'%20dominant-baseline='middle'%3EBild%3C/text%3E%3C/svg%3E";
|
|
1114
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<CarouselComponent, never>;
|
|
1115
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<CarouselComponent, "cds-carousel", never, { "slides": { "alias": "slides"; "required": true; "isSignal": true; }; "active": { "alias": "active"; "required": false; "isSignal": true; }; "hero": { "alias": "hero"; "required": false; "isSignal": true; }; }, { "active": "activeChange"; }, never, never, true, never>;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/**
|
|
1119
|
+
* Theme-Segment — Segment-Switch für das Farbthema. Vorgesehener Einsatz: als
|
|
1120
|
+
* eigenständiges Element zum Hovern. Ist daher IMMER responsiv (unter 640px
|
|
1121
|
+
* Icon-only) und IMMER animiert (Aktiv-Markierung gleitet als Thumb) — beides
|
|
1122
|
+
* fest, nicht konfigurierbar. Icon + Textlabel stehen immer nebeneinander.
|
|
1123
|
+
*
|
|
1124
|
+
* Einzige Konfiguration: `showSystem` (tri Hell/Dunkel/System vs. binär Hell/Dunkel).
|
|
1125
|
+
*
|
|
1126
|
+
* Baut auf den CSS-Kern-Klassen `.theme-bar`/`.tbtn` auf. Jeder Button trägt ein
|
|
1127
|
+
* `aria-label`, ist also auch im Icon-only-Modus benannt. Der Thumb wird per
|
|
1128
|
+
* getBoundingClientRect an die aktive Zelle gesetzt (ResizeObserver misst bei
|
|
1129
|
+
* Breakpoint-/Font-Änderungen nach).
|
|
1130
|
+
*/
|
|
1131
|
+
declare class ThemeSegmentComponent {
|
|
1132
|
+
/** true → Hell/Dunkel/System (tri), false → nur Hell/Dunkel (binär). */
|
|
1133
|
+
readonly showSystem: _angular_core.InputSignal<boolean>;
|
|
1134
|
+
/** @internal */
|
|
1135
|
+
protected readonly svc: ThemeModeService;
|
|
1136
|
+
/** @internal */
|
|
1137
|
+
protected readonly icon: Record<_conciso_design_system_angular.CdsThemeMode, string>;
|
|
1138
|
+
/** @internal */
|
|
1139
|
+
protected readonly label: Record<_conciso_design_system_angular.CdsThemeMode, string>;
|
|
1140
|
+
/** @internal */
|
|
1141
|
+
protected readonly order: _angular_core.Signal<_conciso_design_system_angular.CdsThemeMode[]>;
|
|
1142
|
+
private readonly bar;
|
|
1143
|
+
private readonly thumb;
|
|
1144
|
+
private readonly opts;
|
|
1145
|
+
private readonly destroyRef;
|
|
1146
|
+
constructor();
|
|
1147
|
+
/** Thumb-Geometrie relativ zur Leiste messen (reines Lesen, keine Schreibzugriffe). */
|
|
1148
|
+
private measureThumb;
|
|
1149
|
+
/** Gemessene Geometrie auf den Thumb schreiben (reines Schreiben, kein Lesen). */
|
|
1150
|
+
private applyThumb;
|
|
1151
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ThemeSegmentComponent, never>;
|
|
1152
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ThemeSegmentComponent, "cds-theme-segment", never, { "showSystem": { "alias": "showSystem"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
/**
|
|
1156
|
+
* Theme-Dropdown — Theme-Umschalter auf Basis unseres Custom Select (cds-select):
|
|
1157
|
+
* gestylte Einzelauswahl mit Listbox-Popup und Häkchen. Vorgesehener Einsatz: nur in
|
|
1158
|
+
* den Einstellungen (Settings), NICHT als persistentes Element auf allen Seiten.
|
|
1159
|
+
*
|
|
1160
|
+
* `showSystem` schaltet zwischen Hell/Dunkel/System und binär Hell/Dunkel.
|
|
1161
|
+
*/
|
|
1162
|
+
declare class ThemeSelectComponent {
|
|
1163
|
+
/** true → Hell/Dunkel/System, false → nur Hell/Dunkel. */
|
|
1164
|
+
readonly showSystem: _angular_core.InputSignal<boolean>;
|
|
1165
|
+
/** @internal */
|
|
1166
|
+
protected readonly svc: ThemeModeService;
|
|
1167
|
+
/** @internal */
|
|
1168
|
+
protected readonly options: _angular_core.Signal<CdsSelectOption[]>;
|
|
1169
|
+
/** @internal */
|
|
1170
|
+
protected onChange(value: string | undefined): void;
|
|
1171
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ThemeSelectComponent, never>;
|
|
1172
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ThemeSelectComponent, "cds-theme-select", never, { "showSystem": { "alias": "showSystem"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
/**
|
|
1176
|
+
* Card — Wrapper um `.card` aus css/components.css → „Cards“.
|
|
1177
|
+
*
|
|
1178
|
+
* Die Karte rendert `<article class="card">` und ruht damit flach mit Rahmen. Das folgt der
|
|
1179
|
+
* Konvention „Elevation = Interaktivität“: Schatten trägt eine Karte nur, wenn die Karte selbst
|
|
1180
|
+
* der klickbare Bereich ist. Hier liegt die Interaktion in der Footer-Aktion, nicht in der Fläche.
|
|
1181
|
+
* `.card-elevated` wirkt im CSS ausschließlich auf `a.card-elevated`; eine Link-Karten-Variante
|
|
1182
|
+
* (`<a>` mit Ziel statt `<article>` mit Button) ist deshalb eine eigene Komponente/Variante.
|
|
1183
|
+
*
|
|
1184
|
+
* Bildet die im CSS vorgesehene Struktur ab: .card-media (bereichsgefärbt via
|
|
1185
|
+
* [data-area]) mit der echten Bereichs-Glyphe, .card-body mit .card-eyebrow /
|
|
1186
|
+
* .card-title / .card-text und ein optionales .card-footer mit einer Text-Aktion.
|
|
1187
|
+
*
|
|
1188
|
+
* Alle Textteile sind optional (leer = ausgeblendet); zusätzlich nimmt `.card-body`
|
|
1189
|
+
* beliebigen projizierten Inhalt via `<ng-content>` auf — so eignet sich die Karte
|
|
1190
|
+
* auch für freie Listen-/Detail-Inhalte in SPAs, nicht nur die Marketing-Struktur.
|
|
1191
|
+
* Die Footer-Aktion meldet Klicks über `actionClick`.
|
|
1192
|
+
*/
|
|
1193
|
+
declare class CardComponent {
|
|
1194
|
+
private readonly sanitizer;
|
|
1195
|
+
/** Kicker-Text oberhalb des Titels (leer = keine Eyebrow-Zeile). */
|
|
1196
|
+
readonly eyebrow: _angular_core.InputSignal<string>;
|
|
1197
|
+
/** Kartentitel. */
|
|
1198
|
+
readonly title: _angular_core.InputSignal<string>;
|
|
1199
|
+
/** Anreißer-/Beschreibungstext der Karte. */
|
|
1200
|
+
readonly text: _angular_core.InputSignal<string>;
|
|
1201
|
+
/** Markenbereich → data-area (färbt Media-Glyphe + Eyebrow). */
|
|
1202
|
+
readonly area: _angular_core.InputSignal<CdsArea | undefined>;
|
|
1203
|
+
/** Bereichsgefärbte Medienfläche mit Bereichs-Glyphe anzeigen. */
|
|
1204
|
+
readonly showMedia: _angular_core.InputSignal<boolean>;
|
|
1205
|
+
/** Kompakte Text-Aktion im Footer (leer = kein Footer). */
|
|
1206
|
+
readonly actionLabel: _angular_core.InputSignal<string>;
|
|
1207
|
+
/** Klick auf die Footer-Aktion. */
|
|
1208
|
+
readonly actionClick: _angular_core.OutputEmitterRef<void>;
|
|
1209
|
+
/** @internal */
|
|
1210
|
+
protected readonly actionClasses: _angular_core.Signal<string>;
|
|
1211
|
+
/**
|
|
1212
|
+
* Echte Bereichs-Glyphe aus icons/icons.js, als ico-48-SVG in die Media-Fläche.
|
|
1213
|
+
*
|
|
1214
|
+
* @internal
|
|
1215
|
+
*/
|
|
1216
|
+
protected readonly mediaSvg: _angular_core.Signal<SafeHtml>;
|
|
1217
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<CardComponent, never>;
|
|
1218
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<CardComponent, "cds-card", never, { "eyebrow": { "alias": "eyebrow"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": true; "isSignal": true; }; "text": { "alias": "text"; "required": true; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; "showMedia": { "alias": "showMedia"; "required": false; "isSignal": true; }; "actionLabel": { "alias": "actionLabel"; "required": false; "isSignal": true; }; }, { "actionClick": "actionClick"; }, never, ["*"], true, never>;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
/**
|
|
1222
|
+
* StatCard — Wrapper um `.card-stat` aus css/components.css → „Stat Card“.
|
|
1223
|
+
*
|
|
1224
|
+
* Flache, statische Kennzahlen-Karte mit bereichsgefärbtem Top-Akzent (data-area),
|
|
1225
|
+
* großem Wert (.card-stat-value), Label (.card-stat-label) und optionalem
|
|
1226
|
+
* Trend-Pill (.card-stat-trend.up/.down) inkl. Richtungs-Pfeil — wie docs/index.html.
|
|
1227
|
+
*/
|
|
1228
|
+
declare class StatCardComponent {
|
|
1229
|
+
/** Anzeigewert der Kennzahl. */
|
|
1230
|
+
readonly value: _angular_core.InputSignal<string>;
|
|
1231
|
+
/** Beschriftung der Kennzahl. */
|
|
1232
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
1233
|
+
/** Markenbereich → data-area (Top-Akzent + Wertfarbe). */
|
|
1234
|
+
readonly area: _angular_core.InputSignal<CdsArea>;
|
|
1235
|
+
/** Trendrichtung → .card-stat-trend.up / .down (leer = kein Pill). */
|
|
1236
|
+
readonly trend: _angular_core.InputSignal<"up" | "down" | undefined>;
|
|
1237
|
+
/** Text neben dem Trend-Pfeil (nur sichtbar, wenn `trend` gesetzt ist). */
|
|
1238
|
+
readonly trendText: _angular_core.InputSignal<string>;
|
|
1239
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<StatCardComponent, never>;
|
|
1240
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<StatCardComponent, "cds-stat-card", never, { "value": { "alias": "value"; "required": true; "isSignal": true; }; "label": { "alias": "label"; "required": true; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; "trend": { "alias": "trend"; "required": false; "isSignal": true; }; "trendText": { "alias": "trendText"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
/** Eine Kennzahl im Stat-Strip. */
|
|
1244
|
+
interface CdsFlatStat {
|
|
1245
|
+
area?: CdsArea;
|
|
1246
|
+
value: string;
|
|
1247
|
+
label: string;
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* StatStrip — Wrapper um `.card-stat-strip` + `.card-stat-flat` aus
|
|
1251
|
+
* css/components.css → „Stat-Strip“. Flacher Bandstreifen (auto-fit-Grid auf
|
|
1252
|
+
* --n-50-Grund) mit zentrierten Kennzahlen ohne Rahmen/Schatten; Wert je
|
|
1253
|
+
* Bereich getönt via [data-area]. Genau wie docs/index.html.
|
|
1254
|
+
*/
|
|
1255
|
+
declare class StatStripComponent {
|
|
1256
|
+
/** Kennzahlen des Streifens. */
|
|
1257
|
+
readonly stats: _angular_core.InputSignal<CdsFlatStat[]>;
|
|
1258
|
+
/** Abgerundete Ecken (--r-lg) wie in der Doku-Verwendung. */
|
|
1259
|
+
readonly rounded: _angular_core.InputSignal<boolean>;
|
|
1260
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<StatStripComponent, never>;
|
|
1261
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<StatStripComponent, "cds-stat-strip", never, { "stats": { "alias": "stats"; "required": true; "isSignal": true; }; "rounded": { "alias": "rounded"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
/**
|
|
1265
|
+
* Blockquote — Wrapper um `.bq` aus css/components.css → „Blockquote“.
|
|
1266
|
+
*
|
|
1267
|
+
* Bereichsgefärbtes Zitat mit linker Akzentleiste und getöntem Grund (data-area),
|
|
1268
|
+
* Quote-Icon (ui-quote, dieselbe Glyphe wie docs/index.html), Zitat und
|
|
1269
|
+
* Caption (Name/Rolle). Nur bestehende Klassen.
|
|
1270
|
+
*
|
|
1271
|
+
* **Nicht `cds-article-pullquote` (`.article-pullquote`).** Dieses Bauteil ist
|
|
1272
|
+
* ein eigenständiges Zitat: eine dritte, benannte Person (`name`/`roleLabel` sind
|
|
1273
|
+
* Pflicht), mit getönter Akzent-Box und Quote-Icon. `cds-article-pullquote` ist
|
|
1274
|
+
* dagegen ein typografischer Akzent im eigenen Lauftext eines Wissensbeitrags,
|
|
1275
|
+
* ohne Attribution, ohne Box, ohne Icon — der Autor zitiert dort einen Satz aus
|
|
1276
|
+
* dem eigenen Text, nicht eine dritte Stimme. Volle Abgrenzung samt Tabelle in
|
|
1277
|
+
* dessen Klassendoku (`article-pullquote.component.ts`).
|
|
1278
|
+
*/
|
|
1279
|
+
declare class BlockquoteComponent {
|
|
1280
|
+
private readonly sanitizer;
|
|
1281
|
+
/** Zitattext. */
|
|
1282
|
+
readonly quote: _angular_core.InputSignal<string>;
|
|
1283
|
+
/** Name der zitierten Person. */
|
|
1284
|
+
readonly name: _angular_core.InputSignal<string>;
|
|
1285
|
+
/** Rolle/Funktion der zitierten Person (leer = keine Caption-Zeile dafür). */
|
|
1286
|
+
readonly roleLabel: _angular_core.InputSignal<string>;
|
|
1287
|
+
/** Markenbereich → data-area (Akzentleiste + getönter Grund). */
|
|
1288
|
+
readonly area: _angular_core.InputSignal<CdsArea>;
|
|
1289
|
+
/**
|
|
1290
|
+
* Zitat-Icon aus der zentralen Icon-Registry (icons.ts) statt dreifach
|
|
1291
|
+
* dupliziertem SVG-Pfad in Blockquote/Testimonial/TeamVoice.
|
|
1292
|
+
*
|
|
1293
|
+
* @internal
|
|
1294
|
+
*/
|
|
1295
|
+
protected readonly quoteIcon: _angular_core.Signal<SafeHtml>;
|
|
1296
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<BlockquoteComponent, never>;
|
|
1297
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<BlockquoteComponent, "cds-blockquote", never, { "quote": { "alias": "quote"; "required": true; "isSignal": true; }; "name": { "alias": "name"; "required": true; "isSignal": true; }; "roleLabel": { "alias": "roleLabel"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
/**
|
|
1301
|
+
* CodeBlock — Wrapper um `.cb-wrap` aus css/components.css → „Code-Block“.
|
|
1302
|
+
*
|
|
1303
|
+
* Header mit Sprach-Label (.cb-lang) und optionalem Kopier-Button (.cb-copy),
|
|
1304
|
+
* darunter der Code in `pre.cb-body` (white-space:pre, horizontal scrollbar).
|
|
1305
|
+
* Optionale Terminal-Variante (.cb-terminal). Syntax-Highlighting-Spans
|
|
1306
|
+
* (.k/.v/.s/…) werden hier bewusst nicht gesetzt — der Code bleibt Klartext.
|
|
1307
|
+
*
|
|
1308
|
+
* A11y (siehe docs/komponenten/code-block-verwendung.mdx, Abschnitt „Barrierefreiheit“):
|
|
1309
|
+
* - `<code>` im `<pre>` als semantisches Grundgerüst, damit Screenreader den Inhalt
|
|
1310
|
+
* als „Code“ ankündigen. `.cb-body code{font:inherit}` in components.css fängt den
|
|
1311
|
+
* Browser-Default (`code{font-family:monospace}`) ab, sonst würde das verschachtelte
|
|
1312
|
+
* `<code>` eine andere Schrift als der Rest des Blocks zeigen.
|
|
1313
|
+
* - `tabindex="0"` auf `pre.cb-body`, weil der Block bei langen Zeilen horizontal
|
|
1314
|
+
* scrollt und sonst per Tastatur nicht erreichbar wäre.
|
|
1315
|
+
* - `aria-label="Code kopieren"` auf dem Kopier-Button ist **stabil** und wechselt nach
|
|
1316
|
+
* dem Klick nicht mehr. Ein Bedienelement trägt den Namen seiner Funktion, nicht den
|
|
1317
|
+
* seines letzten Ereignisses — sonst findet man den Button per Sprachsteuerung danach
|
|
1318
|
+
* nicht mehr, und eine Elementliste zeigt einen Button „Code kopiert“, der tatsächlich
|
|
1319
|
+
* kopiert. Die Erfolgsmeldung läuft stattdessen über eine eigene, visuell versteckte
|
|
1320
|
+
* Live-Region (`.sr-only` + `role="status"`, Muster aus `snackbar.component.ts`), die
|
|
1321
|
+
* von Anfang an im DOM steht und nur ihren Textinhalt wechselt: `aria-live` beobachtet
|
|
1322
|
+
* Inhalts-, keine Attribut-Änderungen, und eine Region, die erst beim Klick entsteht,
|
|
1323
|
+
* kündigt bei vielen Screenreadern nichts an. Zusätzlich verhalten sich Live-Regionen
|
|
1324
|
+
* auf dem gerade fokussierten Element (hier: der Button nach dem Klick) uneinheitlich.
|
|
1325
|
+
* Der sichtbare Button-Text wechselt weiterhin „Kopieren“ ↔ „Kopiert!“, ist aber durch
|
|
1326
|
+
* `aria-label` für die Namensberechnung überschrieben und rein optisches Feedback.
|
|
1327
|
+
*/
|
|
1328
|
+
declare class CodeBlockComponent {
|
|
1329
|
+
/** Sprach-Label im Header (.cb-lang), rein informativ, ohne Syntax-Highlighting. */
|
|
1330
|
+
readonly lang: _angular_core.InputSignal<string>;
|
|
1331
|
+
/** Anzuzeigender Code (Klartext, `pre`-formatiert). */
|
|
1332
|
+
readonly code: _angular_core.InputSignal<string>;
|
|
1333
|
+
/** Terminal-Optik → .cb-terminal. */
|
|
1334
|
+
readonly terminal: _angular_core.InputSignal<boolean>;
|
|
1335
|
+
/** Kopier-Button anzeigen (.cb-copy). */
|
|
1336
|
+
readonly copyable: _angular_core.InputSignal<boolean>;
|
|
1337
|
+
/** @internal */
|
|
1338
|
+
protected readonly copied: _angular_core.WritableSignal<boolean>;
|
|
1339
|
+
private readonly destroyRef;
|
|
1340
|
+
/** Reset-Timer des „Kopiert!“-Feedbacks; gemerkt, um ihn zu clearen. */
|
|
1341
|
+
private resetTimer?;
|
|
1342
|
+
constructor();
|
|
1343
|
+
/** @internal */
|
|
1344
|
+
protected readonly wrapClasses: _angular_core.Signal<"cb-wrap cb-terminal" | "cb-wrap">;
|
|
1345
|
+
/**
|
|
1346
|
+
* Text der Live-Region (`role="status"`) neben dem Button. Leer im Ruhezustand, damit
|
|
1347
|
+
* ein erneuter Klick nach Ablauf des Reset-Timers wieder von „“ auf „Code kopiert.“
|
|
1348
|
+
* wechselt und so erneut als Änderung erkannt und angekündigt wird.
|
|
1349
|
+
*
|
|
1350
|
+
* @internal
|
|
1351
|
+
*/
|
|
1352
|
+
protected readonly copyStatus: _angular_core.Signal<"" | "Code kopiert.">;
|
|
1353
|
+
/** @internal */
|
|
1354
|
+
protected copy(): void;
|
|
1355
|
+
private clearResetTimer;
|
|
1356
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<CodeBlockComponent, never>;
|
|
1357
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<CodeBlockComponent, "cds-code-block", never, { "lang": { "alias": "lang"; "required": false; "isSignal": true; }; "code": { "alias": "code"; "required": true; "isSignal": true; }; "terminal": { "alias": "terminal"; "required": false; "isSignal": true; }; "copyable": { "alias": "copyable"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
/** Ein FAQ-Eintrag. */
|
|
1361
|
+
interface CdsFaqItem {
|
|
1362
|
+
/** Frage (Summary-Zeile). */
|
|
1363
|
+
q: string;
|
|
1364
|
+
/** Antworttext. */
|
|
1365
|
+
a: string;
|
|
1366
|
+
}
|
|
1367
|
+
/**
|
|
1368
|
+
* Faq — Wrapper um `.ep-faq` aus css/components.css → „FAQ-Accordion“.
|
|
1369
|
+
*
|
|
1370
|
+
* Nutzt natives `<details>/<summary>` (kein JS nötig): Auf-/Zuklappen, Tastatur
|
|
1371
|
+
* und Zugänglichkeit kommen vom Browser. Der Caret (.ep-faq-caret) dreht via
|
|
1372
|
+
* `details[open]`. Antwort in `.ep-faq-a`.
|
|
1373
|
+
*/
|
|
1374
|
+
declare class FaqComponent {
|
|
1375
|
+
/** Fragen-/Antworten-Liste des Akkordeons. */
|
|
1376
|
+
readonly items: _angular_core.InputSignal<CdsFaqItem[]>;
|
|
1377
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FaqComponent, never>;
|
|
1378
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FaqComponent, "cds-faq", never, { "items": { "alias": "items"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
/**
|
|
1382
|
+
* Skala (cds-scale) — Slider für GEORDNETE (ordinale) Kategorien, deren Labels die
|
|
1383
|
+
* Werte SIND (z. B. Niedrig < Mittel < Hoch, nie < selten < oft < immer).
|
|
1384
|
+
*
|
|
1385
|
+
* Basiert auf dem nativen <input type="range">; min=0, max=labels.length-1, step=1
|
|
1386
|
+
* werden intern abgeleitet und NICHT exponiert. Der aktuelle Wert erscheint als
|
|
1387
|
+
* LABEL im <output> und wird Screenreadern über `aria-valuetext` als Text gemeldet
|
|
1388
|
+
* (nicht als Zahl) — das anerkannte APG-Muster für ordinale Slider. Die Skalenpunkte
|
|
1389
|
+
* zeigen alle Labels, exakt auf ihre Position ausgerichtet und bewusst OHNE
|
|
1390
|
+
* Reduktion (jedes Label ist eine wählbare Stufe).
|
|
1391
|
+
*
|
|
1392
|
+
* NUR für geordnete Kategorien. Ungeordnete/gleichrangige Optionen (ohne natürliche
|
|
1393
|
+
* Reihenfolge) gehören NICHT auf einen Slider → cds-radio-group oder cds-area-tabs.
|
|
1394
|
+
*
|
|
1395
|
+
* Als `ControlValueAccessor` direkt an Angular-Formulare anbindbar (`[(ngModel)]`,
|
|
1396
|
+
* `formControlName`); der Formularwert ist der Stufen-INDEX. Ohne Formular geht
|
|
1397
|
+
* `[(value)]` (valueChange via model()).
|
|
1398
|
+
*/
|
|
1399
|
+
declare class ScaleComponent extends CvaBase<number> {
|
|
1400
|
+
/** Feld-Label (die Frage), z. B. „Zufriedenheit“. */
|
|
1401
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
1402
|
+
/** Geordnete Skalen-Labels; sie sind zugleich die Werte. */
|
|
1403
|
+
readonly labels: _angular_core.InputSignal<string[]>;
|
|
1404
|
+
/** Gewählte Stufe als Index (0-basiert). Two-Way (`[(value)]`) UND Angular-Forms. */
|
|
1405
|
+
readonly value: _angular_core.ModelSignal<number>;
|
|
1406
|
+
/** Markenbereich → Thumb-/Output-Farbe. */
|
|
1407
|
+
readonly area: _angular_core.InputSignal<CdsArea>;
|
|
1408
|
+
readonly helper: _angular_core.InputSignal<string>;
|
|
1409
|
+
/** Deaktiviert; auch über Angular-Forms (setDisabledState) steuerbar. */
|
|
1410
|
+
readonly disabled: _angular_core.ModelSignal<boolean>;
|
|
1411
|
+
readonly scaleId: _angular_core.InputSignal<string>;
|
|
1412
|
+
constructor();
|
|
1413
|
+
/** @internal */
|
|
1414
|
+
protected normalizeValue(value: number): number;
|
|
1415
|
+
/** @internal */
|
|
1416
|
+
protected applyValue(value: number): void;
|
|
1417
|
+
/** @internal */
|
|
1418
|
+
protected applyDisabled(disabled: boolean): void;
|
|
1419
|
+
/**
|
|
1420
|
+
* Oberer Index (aus der Label-Zahl abgeleitet, nicht exponiert).
|
|
1421
|
+
*
|
|
1422
|
+
* @internal
|
|
1423
|
+
*/
|
|
1424
|
+
protected readonly max: _angular_core.Signal<number>;
|
|
1425
|
+
/** @internal */
|
|
1426
|
+
protected readonly currentLabel: _angular_core.Signal<string>;
|
|
1427
|
+
/** @internal */
|
|
1428
|
+
protected readonly sliderClasses: _angular_core.Signal<string>;
|
|
1429
|
+
/** @internal */
|
|
1430
|
+
protected readonly outputClasses: _angular_core.Signal<string>;
|
|
1431
|
+
/**
|
|
1432
|
+
* Alle Labels + Positions-Style. Mitte-Labels sind auf ihrer Thumb-Position
|
|
1433
|
+
* zentriert; das ERSTE/LETZTE Label wird an der jeweiligen Kante verankert
|
|
1434
|
+
* (linksbündig bei 11px bzw. rechtsbündig bei Breite−11px), damit lange
|
|
1435
|
+
* Kategorie-Labels an den Enden nicht über den Rand hinaus abgeschnitten werden.
|
|
1436
|
+
* (11px = halbe Thumb-Breite, siehe css/components.css.)
|
|
1437
|
+
*
|
|
1438
|
+
* @internal
|
|
1439
|
+
*/
|
|
1440
|
+
protected readonly tickItems: _angular_core.Signal<{
|
|
1441
|
+
label: string;
|
|
1442
|
+
style: Record<string, string>;
|
|
1443
|
+
}[]>;
|
|
1444
|
+
/** @internal */
|
|
1445
|
+
protected onInput(event: Event): void;
|
|
1446
|
+
/** @internal */
|
|
1447
|
+
protected markTouched(): void;
|
|
1448
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ScaleComponent, never>;
|
|
1449
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ScaleComponent, "cds-scale", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "labels": { "alias": "labels"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; "helper": { "alias": "helper"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "scaleId": { "alias": "scaleId"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "disabled": "disabledChange"; }, never, never, true, never>;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
type CdsSnackTone = 'def' | 'ok' | 'err';
|
|
1453
|
+
/**
|
|
1454
|
+
* Snackbar — Wrapper um `.snack` aus css/components.css → „Snackbar“.
|
|
1455
|
+
*
|
|
1456
|
+
* Kurze Statusmeldung in drei Tönen (.snack-def / .snack-ok / .snack-err) mit
|
|
1457
|
+
* führendem, tonfarbenem Icon (wie die Doku) und optionaler Aktion (.snack-act).
|
|
1458
|
+
* Ton „err“ ist eine echte Fehlermeldung → role="alert" + aria-live="assertive";
|
|
1459
|
+
* def/ok bleiben role="status" + aria-live="polite" (wie im Doku-Markup).
|
|
1460
|
+
* Konsumiert nur bestehende Klassen.
|
|
1461
|
+
*/
|
|
1462
|
+
declare class SnackbarComponent {
|
|
1463
|
+
/** Anzuzeigender Statustext. */
|
|
1464
|
+
readonly message: _angular_core.InputSignal<string>;
|
|
1465
|
+
/** Ton → .snack-def / .snack-ok / .snack-err */
|
|
1466
|
+
readonly tone: _angular_core.InputSignal<CdsSnackTone>;
|
|
1467
|
+
/** Optionaler Aktions-Button (.snack-act); leer = keiner. */
|
|
1468
|
+
readonly actionLabel: _angular_core.InputSignal<string>;
|
|
1469
|
+
/** Klick auf die optionale Aktion. */
|
|
1470
|
+
readonly action: _angular_core.OutputEmitterRef<void>;
|
|
1471
|
+
/** @internal */
|
|
1472
|
+
protected readonly classes: _angular_core.Signal<string>;
|
|
1473
|
+
/** @internal */
|
|
1474
|
+
protected readonly isError: _angular_core.Signal<boolean>;
|
|
1475
|
+
/**
|
|
1476
|
+
* Tonfarbe des Icons (wie im Doku-Markup).
|
|
1477
|
+
*
|
|
1478
|
+
* @internal
|
|
1479
|
+
*/
|
|
1480
|
+
protected readonly iconStroke: _angular_core.Signal<string>;
|
|
1481
|
+
/**
|
|
1482
|
+
* Lucide-artiger Pfad je Ton (Glühbirne / Häkchen-Kreis / Ausrufezeichen-Kreis).
|
|
1483
|
+
*
|
|
1484
|
+
* @internal
|
|
1485
|
+
*/
|
|
1486
|
+
protected readonly iconPath: _angular_core.Signal<string>;
|
|
1487
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SnackbarComponent, never>;
|
|
1488
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SnackbarComponent, "cds-snackbar", never, { "message": { "alias": "message"; "required": true; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "actionLabel": { "alias": "actionLabel"; "required": false; "isSignal": true; }; }, { "action": "action"; }, never, never, true, never>;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
/**
|
|
1492
|
+
* Testimonial — Wrapper um `.testimonial` aus css/components.css → „Testimonial Card“.
|
|
1493
|
+
*
|
|
1494
|
+
* Statische Zitat-Karte mit bereichsgefärbtem Top-Akzent (data-area), Quote-Icon
|
|
1495
|
+
* (.testimonial-icon, fill:currentColor), Zitat (blockquote) und Footer mit Name/Rolle.
|
|
1496
|
+
* Nur bestehende Klassen — kein eigenes CSS.
|
|
1497
|
+
*/
|
|
1498
|
+
declare class TestimonialComponent {
|
|
1499
|
+
private readonly sanitizer;
|
|
1500
|
+
/** Zitattext. */
|
|
1501
|
+
readonly quote: _angular_core.InputSignal<string>;
|
|
1502
|
+
/** Name der zitierten Person. */
|
|
1503
|
+
readonly name: _angular_core.InputSignal<string>;
|
|
1504
|
+
/** Rolle/Funktion der zitierten Person (leer = keine Rollenzeile im Footer). */
|
|
1505
|
+
readonly roleLabel: _angular_core.InputSignal<string>;
|
|
1506
|
+
/** Markenbereich → data-area (Top-Akzent + Icon-Farbe). */
|
|
1507
|
+
readonly area: _angular_core.InputSignal<CdsArea>;
|
|
1508
|
+
/**
|
|
1509
|
+
* Zitat-Icon aus der zentralen Icon-Registry (icons.ts) statt dreifach
|
|
1510
|
+
* dupliziertem SVG-Pfad in Blockquote/Testimonial/TeamVoice.
|
|
1511
|
+
*
|
|
1512
|
+
* @internal
|
|
1513
|
+
*/
|
|
1514
|
+
protected readonly quoteIcon: _angular_core.Signal<SafeHtml>;
|
|
1515
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TestimonialComponent, never>;
|
|
1516
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<TestimonialComponent, "cds-testimonial", never, { "quote": { "alias": "quote"; "required": true; "isSignal": true; }; "name": { "alias": "name"; "required": true; "isSignal": true; }; "roleLabel": { "alias": "roleLabel"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
/**
|
|
1520
|
+
* TeamVoice — Wrapper um `.team-voice` aus css/components.css → „Team-Stimmen“.
|
|
1521
|
+
*
|
|
1522
|
+
* Editoriale Zitat-Reihe mit seitlichem Foto (im Grid abwechselnd links/rechts,
|
|
1523
|
+
* gesteuert per :nth-child innerhalb von `.team-voices`), bereichsgefärbtem
|
|
1524
|
+
* Akzent (data-area), Quote-Icon (ui-quote), Zitat und Name/Rolle. Mehrere
|
|
1525
|
+
* Komponenten in einen `<div class="team-voices">` legen, damit das alternierende
|
|
1526
|
+
* Layout greift (siehe Story).
|
|
1527
|
+
*/
|
|
1528
|
+
declare class TeamVoiceComponent {
|
|
1529
|
+
private readonly sanitizer;
|
|
1530
|
+
/** Zitattext. */
|
|
1531
|
+
readonly quote: _angular_core.InputSignal<string>;
|
|
1532
|
+
/** Name der zitierten Person. */
|
|
1533
|
+
readonly name: _angular_core.InputSignal<string>;
|
|
1534
|
+
/** Rolle/Funktion der zitierten Person (leer = keine Rollenzeile). */
|
|
1535
|
+
readonly roleLabel: _angular_core.InputSignal<string>;
|
|
1536
|
+
/** Markenbereich → data-area (Akzentfarbe + Rahmen). */
|
|
1537
|
+
readonly area: _angular_core.InputSignal<CdsArea>;
|
|
1538
|
+
/** Bild-URL; leer = neutraler Platzhalter (Doku-Assets sind hier nicht eingebunden). */
|
|
1539
|
+
readonly image: _angular_core.InputSignal<string>;
|
|
1540
|
+
/** Alternativtext des Fotos. */
|
|
1541
|
+
readonly imageAlt: _angular_core.InputSignal<string>;
|
|
1542
|
+
/**
|
|
1543
|
+
* Zitat-Icon aus der zentralen Icon-Registry (icons.ts) statt dreifach
|
|
1544
|
+
* dupliziertem SVG-Pfad in Blockquote/Testimonial/TeamVoice.
|
|
1545
|
+
*
|
|
1546
|
+
* @internal
|
|
1547
|
+
*/
|
|
1548
|
+
protected readonly quoteIcon: _angular_core.Signal<SafeHtml>;
|
|
1549
|
+
/**
|
|
1550
|
+
* Neutraler Inline-SVG-Platzhalter, damit Stories ohne externe Assets rendern.
|
|
1551
|
+
*
|
|
1552
|
+
* @internal
|
|
1553
|
+
*/
|
|
1554
|
+
protected readonly placeholder = "data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='600'%20height='450'%3E%3Crect%20width='600'%20height='450'%20fill='%23E8EDED'/%3E%3Ctext%20x='300'%20y='225'%20font-family='sans-serif'%20font-size='22'%20fill='%236E8585'%20text-anchor='middle'%20dominant-baseline='middle'%3ETeamfoto%3C/text%3E%3C/svg%3E";
|
|
1555
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TeamVoiceComponent, never>;
|
|
1556
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<TeamVoiceComponent, "cds-team-voice", never, { "quote": { "alias": "quote"; "required": true; "isSignal": true; }; "name": { "alias": "name"; "required": true; "isSignal": true; }; "roleLabel": { "alias": "roleLabel"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; "image": { "alias": "image"; "required": false; "isSignal": true; }; "imageAlt": { "alias": "imageAlt"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
/**
|
|
1560
|
+
* DownloadCta — Wrapper um `.cta-dl` aus css/components.css → „Download CTA“.
|
|
1561
|
+
*
|
|
1562
|
+
* Aufmerksamkeitsstarker Download-Block mit bereichsgefärbtem Top-Akzent + Icon
|
|
1563
|
+
* (data-area), Eyebrow/Titel/Beschreibung/Meta und zwei Aktionen (primär/sekundär,
|
|
1564
|
+
* über die bestehenden `.btn`-Klassen). Konsumiert nur vorhandene Styles.
|
|
1565
|
+
*/
|
|
1566
|
+
declare class DownloadCtaComponent {
|
|
1567
|
+
/** Markenbereich → data-area (Top-Akzent, Icon-Tönung, Button-Farbe). */
|
|
1568
|
+
readonly area: _angular_core.InputSignal<CdsArea>;
|
|
1569
|
+
/** Kicker-Text oberhalb des Titels (leer = keine Eyebrow-Zeile). */
|
|
1570
|
+
readonly eyebrow: _angular_core.InputSignal<string>;
|
|
1571
|
+
/** Titel des Download-Angebots. */
|
|
1572
|
+
readonly title: _angular_core.InputSignal<string>;
|
|
1573
|
+
/** Beschreibungstext unterhalb des Titels (leer = keine Beschreibung). */
|
|
1574
|
+
readonly desc: _angular_core.InputSignal<string>;
|
|
1575
|
+
/** Zusatzinfo unterhalb der Beschreibung, z. B. Dateiformat/-größe (leer = keine Meta-Zeile). */
|
|
1576
|
+
readonly meta: _angular_core.InputSignal<string>;
|
|
1577
|
+
/** Beschriftung der primären Aktion. */
|
|
1578
|
+
readonly primaryLabel: _angular_core.InputSignal<string>;
|
|
1579
|
+
/** Beschriftung der sekundären Aktion (leer = ausgeblendet). */
|
|
1580
|
+
readonly secondaryLabel: _angular_core.InputSignal<string>;
|
|
1581
|
+
/** Klick auf die primäre Aktion (Haupt-CTA). */
|
|
1582
|
+
readonly primaryClick: _angular_core.OutputEmitterRef<MouseEvent>;
|
|
1583
|
+
/** Klick auf die sekundäre Aktion (nur wenn `secondaryLabel` gesetzt ist). */
|
|
1584
|
+
readonly secondaryClick: _angular_core.OutputEmitterRef<MouseEvent>;
|
|
1585
|
+
/**
|
|
1586
|
+
* Accessible Name der primären Aktion (cta-verwendung.mdx „Barrierefreiheit“: Button-Label
|
|
1587
|
+
* muss Ressource und Format nennen). Der sichtbare Button-Text bleibt `primaryLabel`
|
|
1588
|
+
* (meist knapp, z. B. „Herunterladen“) — er selbst nennt kein Ziel, der Titel steht nur
|
|
1589
|
+
* visuell daneben (`<h3 class="cta-dl-title">`) und erreicht den Accessible Name nicht.
|
|
1590
|
+
* Wer per Button-Liste navigiert, hört sonst nur die generische Aktion ohne Bezug.
|
|
1591
|
+
* Muster wie bei `PillComponent.computedAriaLabel`/Combobox-Remove-Buttons: „Aktion: Ziel“,
|
|
1592
|
+
* Format/Größe aus `meta` in Klammern angehängt, sofern gesetzt.
|
|
1593
|
+
*
|
|
1594
|
+
* @internal
|
|
1595
|
+
*/
|
|
1596
|
+
protected readonly primaryAriaLabel: _angular_core.Signal<string>;
|
|
1597
|
+
/** Analog zu `primaryAriaLabel`, für die sekundäre Aktion.
|
|
1598
|
+
*
|
|
1599
|
+
* @internal
|
|
1600
|
+
*/
|
|
1601
|
+
protected readonly secondaryAriaLabel: _angular_core.Signal<string>;
|
|
1602
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<DownloadCtaComponent, never>;
|
|
1603
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<DownloadCtaComponent, "cds-download-cta", never, { "area": { "alias": "area"; "required": false; "isSignal": true; }; "eyebrow": { "alias": "eyebrow"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": true; "isSignal": true; }; "desc": { "alias": "desc"; "required": false; "isSignal": true; }; "meta": { "alias": "meta"; "required": false; "isSignal": true; }; "primaryLabel": { "alias": "primaryLabel"; "required": true; "isSignal": true; }; "secondaryLabel": { "alias": "secondaryLabel"; "required": false; "isSignal": true; }; }, { "primaryClick": "primaryClick"; "secondaryClick": "secondaryClick"; }, never, never, true, never>;
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
/**
|
|
1607
|
+
* Section (`[cdsSection]`) — Wrapper um `.ep-section` aus css/components.css
|
|
1608
|
+
* (css/components.css:1223–1253, mobiler Innenabstand: Zeile 1228) mit dem optionalen
|
|
1609
|
+
* Kopf-Trio `.ep-section-label` (Kicker) / `.ep-section-h2` (Überschrift) /
|
|
1610
|
+
* `.ep-section-sub` (Lead). Das strukturelle Grundgerüst, in dem auf den
|
|
1611
|
+
* Beispielseiten praktisch jeder andere Baustein sitzt (133 Vorkommen, 85 davon mit
|
|
1612
|
+
* Kopfzeile).
|
|
1613
|
+
*
|
|
1614
|
+
* Alle drei Textteile sind Beiwerk (`input('')`): eine Sektion besteht auch ganz ohne
|
|
1615
|
+
* Kopf, nur aus ihrem projizierten Inhalt (docs/index.html:9378). Der Inhalt selbst
|
|
1616
|
+
* kommt per `<ng-content>` und ist deshalb kein Input.
|
|
1617
|
+
*
|
|
1618
|
+
* **Entscheidung 1 — Attributselektor, der Konsument wählt `<section>` oder `<div>`.**
|
|
1619
|
+
* Erste Fassung war `cds-section` als eigenes Element, das intern selbst entschied:
|
|
1620
|
+
* `<section aria-labelledby="…">`, wenn ein zugänglicher Name verfügbar war, sonst ein
|
|
1621
|
+
* namenloses `<div>`. Das setzte voraus, dass ein `background` auf einem
|
|
1622
|
+
* UMSCHLIESSENDEN Element sitzen musste (siehe Entscheidung 2) — gemessen im laufenden
|
|
1623
|
+
* Storybook (siehe `docs/adr/0008-selektortyp-der-wrapper-komponenten.md`, „Fall 2“):
|
|
1624
|
+
* `getComputedStyle(host).backgroundColor` und `host.getBoundingClientRect()`
|
|
1625
|
+
* meldeten beide „passt“, der Screenshot zeigte trotzdem keine gemalte Fläche — ein
|
|
1626
|
+
* unbekanntes Custom Element ohne eigene Inline-Inhalte, dessen einziges Kind als Block
|
|
1627
|
+
* herausgebrochen wird, hat keine eigene Box zum Malen.
|
|
1628
|
+
*
|
|
1629
|
+
* Die Komponente hängt sich deshalb als Attribut an ein vom Konsumenten geschriebenes
|
|
1630
|
+
* `<section>` oder `<div>`, analog zu `cds-icon-card`
|
|
1631
|
+
* (`icon-card/icon-card.component.ts`). Der Konsument entscheidet über das Tag, ob die
|
|
1632
|
+
* Sektion überhaupt eine `<section>`-Landmark werden KANN; die Komponente entscheidet
|
|
1633
|
+
* nur noch, ob sie `aria-labelledby` setzt (die eigene, gerenderte `.ep-section-h2` hat
|
|
1634
|
+
* Vorrang, sonst — wenn `heading` leer bleibt — die per `labelledBy` übergebene id einer
|
|
1635
|
+
* Überschrift außerhalb der Komponente). Schreibt der Konsument `<section cdsSection>`
|
|
1636
|
+
* ohne Namen, bleibt es bei einer `<section>` ohne `aria-labelledby`: HTML-AAM gibt ihr
|
|
1637
|
+
* dann keine Landmark-Rolle, aber das ist eine Entscheidung des Konsumenten, keine der
|
|
1638
|
+
* Komponente — sie erzwingt kein Tag mehr.
|
|
1639
|
+
*
|
|
1640
|
+
* **Entscheidung 2 — kein `background`-Input, die Fläche darf jetzt aber am Host
|
|
1641
|
+
* sitzen.** Das Mockup setzt Sektionsflächen inline (`style="background:var(--bg-surface)"`
|
|
1642
|
+
* / `--co-50` / …). Das ist der Flächen-Rhythmus zwischen aufeinanderfolgenden
|
|
1643
|
+
* Sektionen einer SEITE (Begründung inkl. Messwerten in docs/index.html:1450, „Warum
|
|
1644
|
+
* nicht die Sektion senken?“) — eine Entscheidung, die nur die Seite treffen kann, weil
|
|
1645
|
+
* nur sie ihre Nachbar-Sektionen kennt. Ein Bauteil kennt seinen Kontext nicht
|
|
1646
|
+
* (CONTRIBUTING.md §7) und bekäme mit einem `background`-Input eine Zuständigkeit, die
|
|
1647
|
+
* ihm nicht zusteht. Der Konsument setzt die Fläche deshalb weiterhin selbst — jetzt
|
|
1648
|
+
* aber direkt auf dem Element, das `cdsSection` trägt (`<section cdsSection
|
|
1649
|
+
* style="background:…">` bzw. `<div cdsSection style="…">`), denn dieses Element IST
|
|
1650
|
+
* `.ep-section`, kein Host mehr davor. Das umschließende Element aus der ersten Fassung
|
|
1651
|
+
* ist damit nicht mehr nötig (siehe Story „Fläche am Host“).
|
|
1652
|
+
*/
|
|
1653
|
+
declare class SectionComponent {
|
|
1654
|
+
/** Kicker-Text über der Überschrift (`.ep-section-label`, leer = keine Kopfzeile). */
|
|
1655
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
1656
|
+
/** Sektions-Überschrift (`.ep-section-h2`, leer = keine Überschrift). */
|
|
1657
|
+
readonly heading: _angular_core.InputSignal<string>;
|
|
1658
|
+
/** Lead-Text unter der Überschrift (`.ep-section-sub`, leer = kein Lead). */
|
|
1659
|
+
readonly sub: _angular_core.InputSignal<string>;
|
|
1660
|
+
/** Markenbereich → `.t-{area}` auf dem Label (Mockup: `class="ep-section-label t-co"`). */
|
|
1661
|
+
readonly area: _angular_core.InputSignal<CdsArea | undefined>;
|
|
1662
|
+
/**
|
|
1663
|
+
* Id einer Überschrift AUSSERHALB der Komponente, die als zugänglicher Name dient,
|
|
1664
|
+
* wenn `heading` leer bleibt (siehe Entscheidung 1 in der Klassendoku).
|
|
1665
|
+
*/
|
|
1666
|
+
readonly labelledBy: _angular_core.InputSignal<string>;
|
|
1667
|
+
private readonly instance;
|
|
1668
|
+
/** @internal */
|
|
1669
|
+
protected readonly headingId: string;
|
|
1670
|
+
/**
|
|
1671
|
+
* Zugänglicher Name der Sektion: die eigene Überschrift hat Vorrang, sonst die von
|
|
1672
|
+
* außen übergebene id. `null`, wenn keins von beidem vorhanden ist — dann setzt die
|
|
1673
|
+
* Komponente kein `aria-labelledby`. Ob der Host dadurch eine Landmark wird, hängt
|
|
1674
|
+
* am Tag, das der Konsument gewählt hat (siehe Entscheidung 1 in der Klassendoku).
|
|
1675
|
+
*
|
|
1676
|
+
* @internal
|
|
1677
|
+
*/
|
|
1678
|
+
protected readonly ariaLabelledBy: _angular_core.Signal<string | null>;
|
|
1679
|
+
/** @internal */
|
|
1680
|
+
protected readonly labelClasses: _angular_core.Signal<string>;
|
|
1681
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SectionComponent, never>;
|
|
1682
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SectionComponent, "section[cdsSection], div[cdsSection]", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "heading": { "alias": "heading"; "required": false; "isSignal": true; }; "sub": { "alias": "sub"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; "labelledBy": { "alias": "labelledBy"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
/**
|
|
1686
|
+
* HeroImage (cds-hero-image) — Wrapper um `.hero-image` aus css/components.css
|
|
1687
|
+
* (css/components.css:851–878) mit optionaler Caption als Gradient-Overlay
|
|
1688
|
+
* (`-caption`, `-caption-eyebrow`, `-caption-title`, `-caption-text`). Vollbreites,
|
|
1689
|
+
* randloses `<figure>` im 21:9-Format, der Standard-Hero auf allen Customer-Pages
|
|
1690
|
+
* (17 Vorkommen in den Beispielseiten). Semantisch ein figure/figcaption-Pattern:
|
|
1691
|
+
* `alt` trägt die faktische Bildbeschreibung, die Caption die redaktionelle
|
|
1692
|
+
* Einordnung (Eyebrow, Titel, Text) — beide sollen sich nach `docs/index.html`
|
|
1693
|
+
* (Abschnitt „In-Article Figure“, dasselbe Prinzip) nicht inhaltlich decken.
|
|
1694
|
+
*
|
|
1695
|
+
* **Entscheidung 1 — kein `<figcaption>` ohne Textinhalt.** Bleiben `eyebrow`,
|
|
1696
|
+
* `heading` und `text` alle leer, entfällt das `<figcaption>`-Element vollständig
|
|
1697
|
+
* statt leer zu rendern (Variante „Hero ohne Caption“, siehe
|
|
1698
|
+
* `storybook-angular/src/docs/seitenmuster/wissensbeitrag.mdx` Abschnitt „Aufbau“,
|
|
1699
|
+
* Zeile 3: Beitrags-Heros zeigen das visuelle Versprechen ohne Marketing-Overlay).
|
|
1700
|
+
* Ein leeres `<figcaption>` wäre totes Markup ohne Zugänglichkeitsnutzen.
|
|
1701
|
+
*
|
|
1702
|
+
* **Entscheidung 2 — `headingLevel` statt fest verdrahtetem `<h1>`.** Das Mockup
|
|
1703
|
+
* (`docs/index.html:9412` u. a.) rendert `.hero-image-caption-title` durchgehend als
|
|
1704
|
+
* `<h1>`, weil der Hero dort das erste Überschriften-Element der Seite ist. Sitzt
|
|
1705
|
+
* der Hero dagegen unter einem eigenen `<h1>` — der Wissensbeitrag hat seinen
|
|
1706
|
+
* Titel bereits im Article-Header (`.article-title`, ebenfalls `<h1>`) —, entstünde
|
|
1707
|
+
* mit einem zweiten `<h1>` eine doppelte Top-Überschrift. `headingLevel` (Default
|
|
1708
|
+
* `1`) macht die Ebene explizit, `.hero-image-caption-title` ist reine CSS-Klasse
|
|
1709
|
+
* und trägt keine Heading-Semantik, das Styling bleibt in beiden Fällen identisch.
|
|
1710
|
+
*
|
|
1711
|
+
* **Entscheidung 3 — `objectPosition` als Style-Binding, die eine Ausnahme von
|
|
1712
|
+
* Spec-Randbedingung 6.** Der Bildausschnitt ist eine Eigenschaft des konkreten
|
|
1713
|
+
* Bildes (Motiv, Kopfposition), nicht der Seite — anders als z. B. eine
|
|
1714
|
+
* Sektionsfläche, die vom Seitenkontext abhängt. `docs/index.html` setzt ihn
|
|
1715
|
+
* deshalb an jedem Hero individuell direkt am `<img>` (z. B. `object-position:center
|
|
1716
|
+
* 70%`, Zeile 2640). Ohne diesen Input wäre die Komponente für Porträt-lastige
|
|
1717
|
+
* Motive unbrauchbar: `object-fit:cover` beschneidet das Bild sobald der
|
|
1718
|
+
* `max-height`-Cap greift (siehe `.hero-image-media`), und ohne Steuerung landet
|
|
1719
|
+
* der Ausschnitt zufällig in der Bildmitte. Als Style-Binding am `<img>`, nicht als
|
|
1720
|
+
* Klasse — es gibt in der CSS-Schicht keine Modifier-Klassen dafür, nur den
|
|
1721
|
+
* Inline-Weg, den das Mockup selbst nutzt.
|
|
1722
|
+
*
|
|
1723
|
+
* **Entscheidung 4 — `tabindex`/`id` sind nicht Teil der Komponente.** Das Mockup
|
|
1724
|
+
* setzt beides am `<figure>` (`id="ep-landing-main" tabindex="-1"`, z. B. Zeile
|
|
1725
|
+
* 9406) als Sprungziel des Skip-Links. Das Sprungziel ist eine Entscheidung der
|
|
1726
|
+
* Seite (welches Element „Hauptinhalt“ ist, hängt vom Seitenaufbau ab, nicht vom
|
|
1727
|
+
* Hero selbst), deshalb setzt der Konsument beides am `<cds-hero-image>`-Host.
|
|
1728
|
+
* Damit dieses Sprungziel eine eigene Box hat, ist der Host per
|
|
1729
|
+
* `:host{display:block}` selbst ein Block (Muster wie `cds-footer-main`): Als
|
|
1730
|
+
* unbekanntes Element wäre er `display:inline` mit herausgebrochenem Block-Kind
|
|
1731
|
+
* und hätte keine brauchbare Box für Fokus und Scroll-Position (ADR-0008, Fall 2).
|
|
1732
|
+
*/
|
|
1733
|
+
declare class HeroImageComponent {
|
|
1734
|
+
/** Bildquelle. */
|
|
1735
|
+
readonly src: _angular_core.InputSignal<string>;
|
|
1736
|
+
/** Alternativtext — Pflicht, das Bild trägt Bedeutung (kein dekoratives Hero-Bild). */
|
|
1737
|
+
readonly alt: _angular_core.InputSignal<string>;
|
|
1738
|
+
/** Eyebrow-Zeile über dem Titel (`.hero-image-caption-eyebrow`, leer = keine Zeile). */
|
|
1739
|
+
readonly eyebrow: _angular_core.InputSignal<string>;
|
|
1740
|
+
/** Titel der Caption (`.hero-image-caption-title`, leer = kein Titel). */
|
|
1741
|
+
readonly heading: _angular_core.InputSignal<string>;
|
|
1742
|
+
/** Text unter dem Titel (`.hero-image-caption-text`, leer = kein Text). */
|
|
1743
|
+
readonly text: _angular_core.InputSignal<string>;
|
|
1744
|
+
/**
|
|
1745
|
+
* Heading-Ebene des gerenderten Titels: `1`, wenn der Hero die erste Überschrift
|
|
1746
|
+
* der Seite trägt (Standard auf Customer-Pages), `2`, wenn er unter einem
|
|
1747
|
+
* eigenen `<h1>` sitzt (Beitrags-Hero unter dem Article-Header, siehe
|
|
1748
|
+
* Entscheidung 2 in der Klassendoku).
|
|
1749
|
+
*/
|
|
1750
|
+
readonly headingLevel: _angular_core.InputSignal<1 | 2>;
|
|
1751
|
+
/**
|
|
1752
|
+
* `loading`-Strategie des Bildes: `true` (Standard) rendert `loading="eager"`,
|
|
1753
|
+
* weil der Hero auf Customer-Pages immer above the fold sitzt. `false` für einen
|
|
1754
|
+
* zweiten, weiter unten liegenden Hero (z. B. innerhalb einer Bildstrecke).
|
|
1755
|
+
*/
|
|
1756
|
+
readonly eager: _angular_core.InputSignal<boolean>;
|
|
1757
|
+
/**
|
|
1758
|
+
* `object-position` direkt am `<img>` (z. B. `'center 70%'`), siehe Entscheidung 3
|
|
1759
|
+
* in der Klassendoku. Leer = Browser-Default `center center`.
|
|
1760
|
+
*/
|
|
1761
|
+
readonly objectPosition: _angular_core.InputSignal<string>;
|
|
1762
|
+
/**
|
|
1763
|
+
* Ob die Caption gerendert wird: nur, wenn mindestens einer der drei Textteile
|
|
1764
|
+
* gesetzt ist (siehe Entscheidung 1 in der Klassendoku).
|
|
1765
|
+
*
|
|
1766
|
+
* @internal
|
|
1767
|
+
*/
|
|
1768
|
+
protected readonly hasCaption: _angular_core.Signal<boolean>;
|
|
1769
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HeroImageComponent, never>;
|
|
1770
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<HeroImageComponent, "cds-hero-image", never, { "src": { "alias": "src"; "required": true; "isSignal": true; }; "alt": { "alias": "alt"; "required": true; "isSignal": true; }; "eyebrow": { "alias": "eyebrow"; "required": false; "isSignal": true; }; "heading": { "alias": "heading"; "required": false; "isSignal": true; }; "text": { "alias": "text"; "required": false; "isSignal": true; }; "headingLevel": { "alias": "headingLevel"; "required": false; "isSignal": true; }; "eager": { "alias": "eager"; "required": false; "isSignal": true; }; "objectPosition": { "alias": "objectPosition"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
/**
|
|
1774
|
+
* Stoerer (cds-stoerer) — eine Kachel für `<cds-stoerer-set>`, Wrapper um `.stoerer`
|
|
1775
|
+
* aus css/components.css (css/components.css:926–974): Typ-Glyph + Thema-Label
|
|
1776
|
+
* (`.stoerer-topic`), Titel über zwei Zeilen (`.stoerer-title`), Meta-Zeile
|
|
1777
|
+
* (`.stoerer-meta`). Die ganze Kachel ist ein `<a>` — Klick auf die Fläche navigiert,
|
|
1778
|
+
* kein Div mit Klick-Handler.
|
|
1779
|
+
*
|
|
1780
|
+
* **Nur innerhalb von `<cds-stoerer-set>` verwendbar.** Diese Komponente rendert ihr
|
|
1781
|
+
* Markup nicht in sich selbst, sondern in ein internes `<ng-template>` und stellt es
|
|
1782
|
+
* dem Set als `TemplateRef` bereit (Begründung: Entscheidung 1 in der Klassendoku von
|
|
1783
|
+
* `StoererSetComponent`, `stoerer-set.component.ts`) — dasselbe Muster wie
|
|
1784
|
+
* `AreaTabComponent` für `cds-area-tabs` (`area-tabs/area-tab.component.ts`). Ohne
|
|
1785
|
+
* umgebendes `<cds-stoerer-set>` bleibt eine Kachel deshalb unsichtbar: Angular hängt
|
|
1786
|
+
* projizierten Inhalt ohne passendes `<ng-content>`-Ziel nicht ins DOM.
|
|
1787
|
+
*
|
|
1788
|
+
* **Entscheidung — Icon als projizierter Inhalt.** Wie in Ticket 05 (`cds-icon-card`,
|
|
1789
|
+
* `.scratch/angular-seitenbausteine/issues/05-icon-karte.md`, Abschnitt „Entscheidung:
|
|
1790
|
+
* Icon als projizierter Inhalt“) sind die Störer-Icons im Mockup wechselnde
|
|
1791
|
+
* Heroicons, keine DS-Bereichsglyphen aus der Registry — Inhalt, nicht Chrom. Deshalb
|
|
1792
|
+
* Projektion über `<ng-content select="[cdsIcon]">` statt eines `icon`-Inputs.
|
|
1793
|
+
* Abweichend von Ticket 05 trägt das projizierte `<svg>` die Klasse `stoerer-icon`
|
|
1794
|
+
* selbst (zusätzlich zu `cdsIcon`, `viewBox`, `aria-hidden="true"`,
|
|
1795
|
+
* `focusable="false"`): `.ep-card-icon` ist dort ein Container, der die Größe per
|
|
1796
|
+
* Nachfahren-Selektor (`.ep-card-icon svg`) an ein unverändertes Kind durchreicht.
|
|
1797
|
+
* `.stoerer-icon` (css/components.css:974) sitzt dagegen direkt auf dem SVG als
|
|
1798
|
+
* Flex-Kind von `.stoerer-topic` — es gibt keinen Container, der die Klasse
|
|
1799
|
+
* stellvertretend tragen könnte, ohne ein neues CSS-Selektorpaar zu erfinden
|
|
1800
|
+
* (verboten, ADR-0001). Der vollständige Kontrakt steht zusätzlich in der Story.
|
|
1801
|
+
*/
|
|
1802
|
+
declare class StoererComponent {
|
|
1803
|
+
/** Thema/Inhaltstyp neben dem Icon (`.stoerer-topic`, z. B. „Nächste Veranstaltung“). */
|
|
1804
|
+
readonly topic: _angular_core.InputSignal<string>;
|
|
1805
|
+
/** Titel des verlinkten Inhalts — redaktionell auf zwei Zeilen gekürzt. */
|
|
1806
|
+
readonly title: _angular_core.InputSignal<string>;
|
|
1807
|
+
/** Linkziel; die Kachel ist vollständig klickbar. */
|
|
1808
|
+
readonly href: _angular_core.InputSignal<string>;
|
|
1809
|
+
/** Meta-Text nach dem Datum (Ort/Lesezeit/Format), leer = kein Meta-Text. */
|
|
1810
|
+
readonly meta: _angular_core.InputSignal<string>;
|
|
1811
|
+
/** ISO-Datum (`JJJJ-MM-TT`) → `<time datetime>`, gerendert vor `meta`, leer = kein Datum. */
|
|
1812
|
+
readonly date: _angular_core.InputSignal<string>;
|
|
1813
|
+
/**
|
|
1814
|
+
* Kachel-Markup als Template, das `cds-stoerer-set` per `ngTemplateOutlet` in sein
|
|
1815
|
+
* eigenes `<li>` einsetzt (siehe Klassendoku).
|
|
1816
|
+
*
|
|
1817
|
+
* @internal
|
|
1818
|
+
*/
|
|
1819
|
+
readonly content: _angular_core.Signal<TemplateRef<unknown>>;
|
|
1820
|
+
/** @internal */
|
|
1821
|
+
protected readonly hasMeta: _angular_core.Signal<boolean>;
|
|
1822
|
+
/**
|
|
1823
|
+
* `date` als deutsches Langdatum („3. Dezember 2026“), manuell aus den
|
|
1824
|
+
* ISO-Komponenten gebaut statt über `new Date(iso)`: Ein reines Datums-ISO-String
|
|
1825
|
+
* wird von der Date-API als UTC-Mitternacht gelesen, `Intl.DateTimeFormat`
|
|
1826
|
+
* formatiert aber in der lokalen Zeitzone — in Zonen westlich von UTC kippt das
|
|
1827
|
+
* angezeigte Datum sonst einen Tag zurück.
|
|
1828
|
+
*
|
|
1829
|
+
* @internal
|
|
1830
|
+
*/
|
|
1831
|
+
protected readonly formattedDate: _angular_core.Signal<string>;
|
|
1832
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<StoererComponent, never>;
|
|
1833
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<StoererComponent, "cds-stoerer", never, { "topic": { "alias": "topic"; "required": true; "isSignal": true; }; "title": { "alias": "title"; "required": true; "isSignal": true; }; "href": { "alias": "href"; "required": true; "isSignal": true; }; "meta": { "alias": "meta"; "required": false; "isSignal": true; }; "date": { "alias": "date"; "required": false; "isSignal": true; }; }, {}, never, ["[cdsIcon]"], true, never>;
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
/**
|
|
1837
|
+
* StoererSet (cds-stoerer-set) — Wrapper um `.stoerer-set` / `.stoerer-list` aus
|
|
1838
|
+
* css/components.css (css/components.css:926–974): das Set aus ein bis drei
|
|
1839
|
+
* `<cds-stoerer>`-Kacheln, das auf der Startseite oben rechts über dem Hero-Bild
|
|
1840
|
+
* liegt. Der Positionsrahmen `.stoerer-hero` (umschließt Hero-Bild UND Set,
|
|
1841
|
+
* `container-type:inline-size` für die Container-Query-Schwelle) ist bewusst NICHT
|
|
1842
|
+
* Teil dieser Komponente, sondern bleibt beim Konsumenten (siehe
|
|
1843
|
+
* `.scratch/angular-seitenbausteine/issues/03-stoerer.md`): er umschließt Hero UND
|
|
1844
|
+
* Set gemeinsam, das könnte diese Komponente als internes Detail nicht leisten, ohne
|
|
1845
|
+
* das Hero-Bild selbst zu kennen.
|
|
1846
|
+
*
|
|
1847
|
+
* **Entscheidung — `<li>` ohne strukturelles Wrapper-Element.** `.stoerer-list` ist
|
|
1848
|
+
* ein `<ul>`; nach HTML-AAM bekommt ein `<li>` die implizite Rolle `listitem` nur als
|
|
1849
|
+
* direktes Kind eines `<ul>`/`<ol>`/`<menu>` — Screenreader kündigen die Liste sonst
|
|
1850
|
+
* nicht mit Anzahl an. Ein einfaches `<ng-content>` hätte `<cds-stoerer>` selbst
|
|
1851
|
+
* zwischen `<ul>` und `<li>` geschoben (kein direktes Kind mehr), und ein
|
|
1852
|
+
* `display:contents`-Wrapper verbietet sich als neues CSS (ADR-0001). Gelöst über
|
|
1853
|
+
* dasselbe Muster, das `cds-area-tabs`/`cds-area-tab` bereits für „projizierte
|
|
1854
|
+
* Kind-Komponente, deren Markup an einer selbst bestimmten Stelle im eigenen Template
|
|
1855
|
+
* landet“ etabliert (`area-tabs/area-tabs.component.ts`): `cds-stoerer` rendert nicht
|
|
1856
|
+
* in sich selbst, sondern in ein `<ng-template>` und liefert es als `TemplateRef`
|
|
1857
|
+
* (`stoerer.component.ts`). Diese Komponente liest die projizierten `<cds-stoerer>`
|
|
1858
|
+
* über `contentChildren()` und setzt ihr Template per `ngTemplateOutlet` direkt in
|
|
1859
|
+
* ein selbst gerendertes `<li>` ein. Im laufenden Storybook geprüft (`Komponenten/Hero/
|
|
1860
|
+
* Störer` → Story „Interaktiv“, DOM live im Browser per `querySelector` abgefragt,
|
|
1861
|
+
* zusätzlich in der Play-Funktion der Story gepinnt): `<ul class="stoerer-list">`
|
|
1862
|
+
* hat ausschließlich `<li>` als direkte Kinder (`ul.stoerer-list > *` → `["LI","LI"]`),
|
|
1863
|
+
* `<cds-stoerer>` selbst taucht im gerenderten DOM nirgends auf — Angular hängt
|
|
1864
|
+
* unprojizierten Content ohne passendes `<ng-content>`-Ziel gar nicht erst ein, exakt
|
|
1865
|
+
* wie bei `<cds-area-tab>` innerhalb von `<cds-area-tabs>`. Ein `items`-Array (die im
|
|
1866
|
+
* Ticket vermerkte Alternative) hätte denselben DOM-Baum ergeben, aber den
|
|
1867
|
+
* Konsumenten gezwungen, das projizierte Icon jeder Kachel in ein Datenobjekt
|
|
1868
|
+
* umzuziehen; mit dieser Lösung bleibt die deklarative, Content-projizierende API
|
|
1869
|
+
* erhalten.
|
|
1870
|
+
*/
|
|
1871
|
+
declare class StoererSetComponent {
|
|
1872
|
+
/** Zugänglicher Name des Sets (`<aside aria-label>`); „Aktuelles“ wie im Mockup. */
|
|
1873
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
1874
|
+
/**
|
|
1875
|
+
* Die Kacheln kommen als projizierte `<cds-stoerer>`-Kinder.
|
|
1876
|
+
*
|
|
1877
|
+
* @internal
|
|
1878
|
+
*/
|
|
1879
|
+
protected readonly tiles: _angular_core.Signal<readonly StoererComponent[]>;
|
|
1880
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<StoererSetComponent, never>;
|
|
1881
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<StoererSetComponent, "cds-stoerer-set", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; }, {}, ["tiles"], never, true, never>;
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
/**
|
|
1885
|
+
* LinkCard (cds-link-card) — Wrapper um `a.card.card-elevated` aus css/components.css
|
|
1886
|
+
* (css/components.css:147–154) samt `.card-cta-link` (css/components.css:1347–1356).
|
|
1887
|
+
*
|
|
1888
|
+
* Die Lücke, die `CardComponent` bewusst offen lässt (siehe deren Klassendoku):
|
|
1889
|
+
* `.card-elevated` wirkt im CSS ausschließlich auf `a.card-elevated` (Riegel aus
|
|
1890
|
+
* `CONTRIBUTING.md` §4, „Elevation = Interaktivität“) — eine `<article>`/`<div>`-Karte
|
|
1891
|
+
* bekommt den Schatten auch mit gesetzter Klasse nicht. Diese Komponente rendert
|
|
1892
|
+
* deshalb ein echtes `<a>`, die ganze Fläche ist der Link, kein Klick-Handler auf
|
|
1893
|
+
* einem nicht-interaktiven Element.
|
|
1894
|
+
*
|
|
1895
|
+
* Struktur wie `CardComponent`: optionale bereichsgefärbte `.card-media` mit der
|
|
1896
|
+
* echten Bereichs-Glyphe, `.card-body` mit `.card-eyebrow` / `.card-title` /
|
|
1897
|
+
* `.card-text`. Neu ist der Fuß `.card-cta-link`, optional unten angeheftet
|
|
1898
|
+
* (`--pinned`, für gleich hohe Karten im Raster über `.card-body{flex:1}`).
|
|
1899
|
+
*
|
|
1900
|
+
* **Entscheidung — Fuß ist ein `<span>`, kein zweites `<a>`.** Die ganze Karte ist
|
|
1901
|
+
* bereits `a.card-elevated`; ein verschachteltes `<a>` im Fuß wäre ungültiges HTML
|
|
1902
|
+
* (der Browser schließt es beim Parsen vorzeitig) und würde zwei Klickziele auf
|
|
1903
|
+
* derselben Fläche erzeugen. `docs/index.html:4707–4716` zeigt exakt dieses Muster:
|
|
1904
|
+
* `<span class="card-cta-link card-cta-link--pinned">…</span>` innerhalb von
|
|
1905
|
+
* `a.card.card-elevated`. `.card-cta-link` selbst ist nicht auf `a.` gescoped (anders
|
|
1906
|
+
* als `.card-elevated`), der Span trägt Farbe und Typografie also unverändert.
|
|
1907
|
+
*
|
|
1908
|
+
* **Entscheidung — Pfeil-Suffix wird von der Komponente ergänzt, nicht Teil von
|
|
1909
|
+
* `ctaLabel`.** Der Pfeil ist reine visuelle Affordanz, kein Inhalt — `docs/index.html`
|
|
1910
|
+
* (Abschnitt „Klickbare Karte“) kapselt ihn deshalb selbst in ein
|
|
1911
|
+
* `aria-hidden`-Span. Läge das Zeichen im `ctaLabel`-Text, müsste jeder Aufrufer
|
|
1912
|
+
* selbst an das `aria-hidden` denken; das wird hier stattdessen einmalig in der
|
|
1913
|
+
* Komponente erledigt.
|
|
1914
|
+
*
|
|
1915
|
+
* **Entscheidung — kein `data-area` auf `.card-cta-link`.** Die Doku hält die
|
|
1916
|
+
* CTA-Farbe in gemischten Bereichs-Listen bewusst einheitlich Corporate-Petrol
|
|
1917
|
+
* (`docs/index.html:8762`: „CTA-Farbe einheitlich `--co-700`“), unabhängig vom
|
|
1918
|
+
* `area` der Karte. Der Fuß bleibt deshalb ohne `data-area`-Bindung.
|
|
1919
|
+
*
|
|
1920
|
+
* **Entscheidung — Host als Flex-Container.** Im Raster ist der Host das
|
|
1921
|
+
* Grid-Kind, das `align-items:stretch` auf Zeilenhöhe zieht, nicht `a.card`. Ohne
|
|
1922
|
+
* eigene Regel blieb die Karte auf Inhaltshöhe, und eine Karte ohne Eyebrow endete
|
|
1923
|
+
* samt `--pinned`-Fuß höher als ihre Nachbarn. `:host{display:flex}` mit
|
|
1924
|
+
* `flex:1` an der Karte gibt die gestreckte Höhe an `a.card` weiter; Story
|
|
1925
|
+
* „Im Raster“ pinnt das.
|
|
1926
|
+
*/
|
|
1927
|
+
declare class LinkCardComponent {
|
|
1928
|
+
private readonly sanitizer;
|
|
1929
|
+
/** Kartentitel. */
|
|
1930
|
+
readonly title: _angular_core.InputSignal<string>;
|
|
1931
|
+
/** Anreißer-/Beschreibungstext der Karte. */
|
|
1932
|
+
readonly text: _angular_core.InputSignal<string>;
|
|
1933
|
+
/** Linkziel; die ganze Kartenfläche ist klickbar. */
|
|
1934
|
+
readonly href: _angular_core.InputSignal<string>;
|
|
1935
|
+
/** Kicker-Text oberhalb des Titels (`.card-eyebrow`, leer = keine Eyebrow-Zeile). */
|
|
1936
|
+
readonly eyebrow: _angular_core.InputSignal<string>;
|
|
1937
|
+
/** Markenbereich → `data-area` (färbt Media-Glyphe + Eyebrow). */
|
|
1938
|
+
readonly area: _angular_core.InputSignal<CdsArea | undefined>;
|
|
1939
|
+
/** Bereichsgefärbte Medienfläche mit Bereichs-Glyphe anzeigen. */
|
|
1940
|
+
readonly showMedia: _angular_core.InputSignal<boolean>;
|
|
1941
|
+
/** Fuß-Link-Text (`.card-cta-link`, leer = kein Fuß). */
|
|
1942
|
+
readonly ctaLabel: _angular_core.InputSignal<string>;
|
|
1943
|
+
/** Fuß unten an die Kartenunterkante anheften (`.card-cta-link--pinned`, für gleich hohe Karten im Raster). */
|
|
1944
|
+
readonly ctaPinned: _angular_core.InputSignal<boolean>;
|
|
1945
|
+
/**
|
|
1946
|
+
* Echte Bereichs-Glyphe aus icons/icons.js, als ico-48-SVG in die Media-Fläche —
|
|
1947
|
+
* identisch zu `CardComponent.mediaSvg`.
|
|
1948
|
+
*
|
|
1949
|
+
* @internal
|
|
1950
|
+
*/
|
|
1951
|
+
protected readonly mediaSvg: _angular_core.Signal<SafeHtml>;
|
|
1952
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<LinkCardComponent, never>;
|
|
1953
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<LinkCardComponent, "cds-link-card", never, { "title": { "alias": "title"; "required": true; "isSignal": true; }; "text": { "alias": "text"; "required": true; "isSignal": true; }; "href": { "alias": "href"; "required": true; "isSignal": true; }; "eyebrow": { "alias": "eyebrow"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; "showMedia": { "alias": "showMedia"; "required": false; "isSignal": true; }; "ctaLabel": { "alias": "ctaLabel"; "required": false; "isSignal": true; }; "ctaPinned": { "alias": "ctaPinned"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
/**
|
|
1957
|
+
* FeaturedCard (cds-featured-card) — Wrapper um `.card-featured` aus
|
|
1958
|
+
* css/components.css (css/components.css:230–249): die horizontale Großkarte für
|
|
1959
|
+
* **genau einen** hervorgehobenen Beitrag oder Termin (Bild links 60 %, Textspalte
|
|
1960
|
+
* rechts 40 % als absolut positioniertes Overlay), siehe `docs/index.html`
|
|
1961
|
+
* Abschnitt „Featured · horizontale Großkarte“ (`#gt-events-featured`).
|
|
1962
|
+
*
|
|
1963
|
+
* **Entscheidung 1 — `href` entscheidet zwischen `<a>` und `<article>`.** Die
|
|
1964
|
+
* Doku-Beispiele zeigen die Featured-Card ausschließlich als Link
|
|
1965
|
+
* (`a.card.card-elevated.card-featured`), das Ticket verlangt aber ausdrücklich eine
|
|
1966
|
+
* Variante ohne `<a>`. `.card-elevated` ist nach `CONTRIBUTING.md` §4 („Elevation =
|
|
1967
|
+
* Interaktivität“) im CSS auf `a.card-elevated` gescoped — eine `<article>` bekäme
|
|
1968
|
+
* den Schatten mit gesetzter Klasse ohnehin nicht. Die Komponente führt die Klasse
|
|
1969
|
+
* deshalb nur mit, wenn sie auch wirkt: mit `href` ein `<a class="card card-elevated
|
|
1970
|
+
* card-featured">`, ohne `href` ein `<article class="card card-featured">` (ruht
|
|
1971
|
+
* flach mit dem `--bd-strong`-Rahmen aus `.card`, wie jede statische Fläche). Media
|
|
1972
|
+
* und Body stehen dafür einmal in einem gemeinsamen `<ng-template>` und werden in
|
|
1973
|
+
* beiden Zweigen per `<ng-container [ngTemplateOutlet]>` eingesetzt — dasselbe
|
|
1974
|
+
* Muster wie `SectionComponent` (`section.component.ts`) für „zwei Wurzelelemente,
|
|
1975
|
+
* ein Körper“. `<ng-container>` rendert selbst kein Element, die Kindselektoren aus
|
|
1976
|
+
* Entscheidung 2 bleiben also unberührt: im laufenden Storybook erneut per
|
|
1977
|
+
* `querySelector`/`getBoundingClientRect` gemessen (siehe Play-Funktionen), Ergebnis
|
|
1978
|
+
* unverändert zur vorherigen, noch duplizierten Fassung.
|
|
1979
|
+
*
|
|
1980
|
+
* **Entscheidung 2 — die Pill ist direkt komponiertes Markup, kein `<cds-pill>`.**
|
|
1981
|
+
* Das CSS arbeitet mit direkten Kindselektoren: `.card-featured>.card-media`,
|
|
1982
|
+
* `.card-featured-body>.pill`, `.card-featured-body>.card-text`,
|
|
1983
|
+
* `.card-featured-body>.card-title-hero` (jeweils mit `margin`/`min-height`/
|
|
1984
|
+
* `line-clamp`-Übernahmen, siehe CSS-Kommentare dort). `<cds-pill>` rendert sein
|
|
1985
|
+
* `.pill`-`<span>` nicht auf dem eigenen Host-Element, sondern eine Ebene tiefer
|
|
1986
|
+
* (`pill.component.ts`: Host ist `<cds-pill>`, `.pill` sitzt im Template darunter) —
|
|
1987
|
+
* projiziert als `<cds-pill class="pill">` stünde die Pille damit als
|
|
1988
|
+
* `.card-featured-body>cds-pill>.pill`, zwei Ebenen statt einer, und die
|
|
1989
|
+
* Kindselektoren griffen nicht mehr (leise: kein Fehler, nur falsches Layout).
|
|
1990
|
+
* Dieselbe Abwägung wie in `download-cta.component.ts` (Kommentar über den
|
|
1991
|
+
* Buttons): dort verhindert `.cta-dl-actions`, das seine Kinder per Flex streckt,
|
|
1992
|
+
* dass ein `<cds-button>`-Host mitstreckt; hier verhindert ein Kindselektor, dass
|
|
1993
|
+
* irgendein zusätzliches Host-Element dazwischentritt. In beiden Fällen werden die
|
|
1994
|
+
* Klassen direkt komponiert statt eine Wrapper-Komponente zu verwenden. Geprüft im
|
|
1995
|
+
* laufenden Storybook (`Komponenten/Cards & Teaser/Featured-Karte`): die gerenderte
|
|
1996
|
+
* Kette ist `.card-featured>.card-media` und `.card-featured-body` mit `.pill`,
|
|
1997
|
+
* `.card-text`, `.card-title-hero` als direkten Kindern, kein zusätzliches Element
|
|
1998
|
+
* dazwischen (siehe Play-Funktionen der Stories).
|
|
1999
|
+
*
|
|
2000
|
+
* Weil die Pille direkt komponiert wird, übernimmt diese Komponente auch die
|
|
2001
|
+
* `aria-label`-Regel von `PillComponent` (`pill.component.ts`) von Hand nach:
|
|
2002
|
+
* Default „Bereich `<pill>`“, überschreibbar über `pillAriaLabel` — nötig, weil die
|
|
2003
|
+
* Pille nicht immer einen Bereich benennt (siehe `pillAriaLabel`-Doku unten).
|
|
2004
|
+
*
|
|
2005
|
+
* **Entscheidung 3 — kein separates `data-area` für die Pille.** Die Doku setzt
|
|
2006
|
+
* Karte und Pille immer auf denselben Bereich (`docs/index.html:8979`: Karte
|
|
2007
|
+
* `data-area="es"`, Pille `data-area="es"`). Die Komponente bindet deshalb `area`
|
|
2008
|
+
* auf beide, statt einen zweiten Bereichs-Input zu erfinden.
|
|
2009
|
+
*
|
|
2010
|
+
* **Entscheidung 4 — kein CTA- und kein Meta-Input.** Die Doku-Vorlage zeigt
|
|
2011
|
+
* zusätzlich einen Meta-Strip (Datum/Ort/Format) und einen `.card-cta-link`-Fuß;
|
|
2012
|
+
* das Ticket benennt als API aber nur `title`/`text`/`imageSrc`/`imageAlt`/`href`/
|
|
2013
|
+
* `pill`/`area`. Diese Komponente bildet exakt diese Fläche ab, ohne Inputs zu
|
|
2014
|
+
* erfinden, die das Ticket nicht vorsieht — ein Meta-Strip oder ein CTA-Fuß wären
|
|
2015
|
+
* eine spätere, eigene Erweiterung.
|
|
2016
|
+
*/
|
|
2017
|
+
declare class FeaturedCardComponent {
|
|
2018
|
+
/** Titel (`.card-title-hero`). */
|
|
2019
|
+
readonly title: _angular_core.InputSignal<string>;
|
|
2020
|
+
/** Anreißer-/Beschreibungstext (`.card-text`). */
|
|
2021
|
+
readonly text: _angular_core.InputSignal<string>;
|
|
2022
|
+
/** Bildquelle der 16:9-Medienfläche. */
|
|
2023
|
+
readonly imageSrc: _angular_core.InputSignal<string>;
|
|
2024
|
+
/** Alternativtext des Bildes — Pflicht, das Bild trägt Bedeutung. */
|
|
2025
|
+
readonly imageAlt: _angular_core.InputSignal<string>;
|
|
2026
|
+
/** Linkziel; gesetzt → `<a>` (ganze Fläche klickbar), leer → `<article>` (statisch, kein Schatten). */
|
|
2027
|
+
readonly href: _angular_core.InputSignal<string>;
|
|
2028
|
+
/** Bereichs-Pille über dem Titel (`.pill`, leer = keine Pille). */
|
|
2029
|
+
readonly pill: _angular_core.InputSignal<string>;
|
|
2030
|
+
/**
|
|
2031
|
+
* `aria-label` der Pille überschreiben; Default „Bereich `<pill>`“, wie
|
|
2032
|
+
* `PillComponent` (`pill.component.ts`). Nötig, weil `pill` nicht immer einen
|
|
2033
|
+
* Bereich benennt — das Mockup zeigt neben `aria-label="Bereich Corporate"` auch
|
|
2034
|
+
* `aria-label="Lesezeit 12 Minuten"` für dieselbe Klasse.
|
|
2035
|
+
*/
|
|
2036
|
+
readonly pillAriaLabel: _angular_core.InputSignal<string | undefined>;
|
|
2037
|
+
/** Markenbereich → `data-area` auf Karte und Pille. */
|
|
2038
|
+
readonly area: _angular_core.InputSignal<CdsArea | undefined>;
|
|
2039
|
+
/** @internal */
|
|
2040
|
+
protected readonly computedPillAriaLabel: _angular_core.Signal<string>;
|
|
2041
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FeaturedCardComponent, never>;
|
|
2042
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FeaturedCardComponent, "cds-featured-card", never, { "title": { "alias": "title"; "required": true; "isSignal": true; }; "text": { "alias": "text"; "required": true; "isSignal": true; }; "imageSrc": { "alias": "imageSrc"; "required": true; "isSignal": true; }; "imageAlt": { "alias": "imageAlt"; "required": true; "isSignal": true; }; "href": { "alias": "href"; "required": false; "isSignal": true; }; "pill": { "alias": "pill"; "required": false; "isSignal": true; }; "pillAriaLabel": { "alias": "pillAriaLabel"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
/**
|
|
2046
|
+
* IconCard (`[cdsIconCard]`) — Wrapper um `.ep-card` aus css/components.css
|
|
2047
|
+
* (css/components.css:1298–1340): die Icon-Kachel mit farbiger Icon-Fläche, Eyebrow,
|
|
2048
|
+
* Titel, Text und optionaler Pfeil-CTA-Zeile, die auf den Beispielseiten Bereichs-
|
|
2049
|
+
* und Angebots-Einstiege trägt (`docs/index.html:9401–9420` u. a., 81 Vorkommen).
|
|
2050
|
+
*
|
|
2051
|
+
* **Entscheidung 1 — Attributselektor, kein Element-Selektor.** Erste Fassung dieser
|
|
2052
|
+
* Komponente war `cds-icon-card` als eigenes Element mit `href`-Input, das intern
|
|
2053
|
+
* zwischen `<a class="ep-card ep-card-link">` und `<div class="ep-card">` wählte
|
|
2054
|
+
* (`@if`/`@else` + `<ng-template>`). Gemessen im laufenden Storybook: zwei Karten mit
|
|
2055
|
+
* unterschiedlich langem Text ergaben im `.ep-cards`-Grid ungleiche `.ep-card`-Höhen
|
|
2056
|
+
* (174px/270px), weil `align-items:stretch` nur den unsichtbaren `<cds-icon-card>`-Host
|
|
2057
|
+
* streckt, nicht das `.ep-card`-Element eine Ebene darunter. Dieselbe Messung mit
|
|
2058
|
+
* `.ep-card` OHNE Host-Wrapper (rohes Markup direkt in `.ep-cards`) ergab 306px/306px,
|
|
2059
|
+
* CTA-Unterkanten auf identischer Y-Koordinate — das CSS gleicht die Höhen korrekt aus,
|
|
2060
|
+
* sobald `.ep-card` selbst das Grid-Kind ist. Der Fehler saß also im Wrapper-Element,
|
|
2061
|
+
* nicht in der CSS-Schicht (siehe „Im Raster“-Story, dort dieselbe Messung als
|
|
2062
|
+
* Play-Funktion gepinnt).
|
|
2063
|
+
*
|
|
2064
|
+
* Die Lösung, analog zu Angular Materials `a[mat-button], button[mat-button]`: die
|
|
2065
|
+
* Komponente hängt sich als Attribut an ein vom Konsumenten geschriebenes `<a>` oder
|
|
2066
|
+
* `<div>`, statt ein eigenes Element zu sein. Dadurch trägt **derselbe** native Knoten,
|
|
2067
|
+
* den der Konsument in sein Grid schreibt, sowohl die Komponentenlogik als auch die
|
|
2068
|
+
* `.ep-card`-Klasse — kein Host-Element dazwischen, `align-items:stretch` trifft direkt
|
|
2069
|
+
* auf `.ep-card`.
|
|
2070
|
+
*
|
|
2071
|
+
* **`href` ist deshalb kein Input mehr.** Ob die Karte ein Link ist, entscheidet der
|
|
2072
|
+
* Konsument über das gewählte Tag (`<a cdsIconCard href="…">` vs. `<div cdsIconCard>`)
|
|
2073
|
+
* und setzt `href` als natives Attribut direkt am `<a>` — Angular muss dafür nichts
|
|
2074
|
+
* verwalten. `.ep-card-link` (nur auf `a.ep-card-link` wirksam, `CONTRIBUTING.md` §4)
|
|
2075
|
+
* wird deshalb vom Host-Tag abgeleitet, nicht mehr per Verzweigung im Template
|
|
2076
|
+
* gerendert.
|
|
2077
|
+
*
|
|
2078
|
+
* **`.ep-card-link` hängt an `<a>` UND `href`, nicht am Tag allein.** Ein `<a
|
|
2079
|
+
* cdsIconCard>` ohne `href` ist weder fokussierbar noch hat es eine Link-Rolle —
|
|
2080
|
+
* `.ep-card-link` (Schatten, Hover-Anhebung, Cursor) nur am Tag festzumachen hätte
|
|
2081
|
+
* eine Karte erzeugt, die aussieht wie ein Link, aber keiner ist, und genau die
|
|
2082
|
+
* Garantie aufgegeben, die die frühere `href`-Input-Fassung strukturell hatte (leerer
|
|
2083
|
+
* String → zwingend der `<div>`-Zweig). `isLink()` prüft deshalb beides.
|
|
2084
|
+
*
|
|
2085
|
+
* **`isLink()` ist eine Methode im Host-Binding, kein einmalig berechnetes Feld.**
|
|
2086
|
+
* `href` kann nach dem Erstellen zur Laufzeit gesetzt/entfernt werden (z. B.
|
|
2087
|
+
* `<a cdsIconCard [attr.href]="urlSignal()">`). Ein Host-Binding-Ausdruck
|
|
2088
|
+
* (`'[class.ep-card-link]': 'isLink()'`) wird bei JEDEM Refresh der DECLARING VIEW
|
|
2089
|
+
* neu ausgewertet — also der Elternvorlage, die `<a cdsIconCard>` schreibt —, nicht
|
|
2090
|
+
* nur bei einem Refresh der eigenen (OnPush-gesteuerten) Kindvorlage dieser
|
|
2091
|
+
* Komponente. Das ist kein Sonderfall wie bei einem in ADR-0007 §3 verworfenen
|
|
2092
|
+
* Getter (dort ging es um einen Getter in einer INTERPOLATION der eigenen
|
|
2093
|
+
* Kindvorlage, die unter OnPush nur bei eigenen Signal-Änderungen neu läuft):
|
|
2094
|
+
* Host-Bindings werden unabhängig vom OnPush-Status der Komponente selbst
|
|
2095
|
+
* mitgeführt, wenn die Elternansicht aktualisiert wird — reagiert also auf
|
|
2096
|
+
* `[attr.href]`/`[href]`-Bindungen des Konsumenten, ob Signal- oder
|
|
2097
|
+
* Zone.js-getrieben, ohne `effect()` (ADR-0007 §4) und ohne `MutationObserver`.
|
|
2098
|
+
* Empirisch geprüft (Spike-Story mit `[attr.href]="signal()"`-Toggle zur Laufzeit,
|
|
2099
|
+
* seither wieder gelöscht): Setzen des Signals nach Erstrender schaltet
|
|
2100
|
+
* `.ep-card-link` zuverlässig um. Einzige Grenze: eine Änderung, die AUSSERHALB von
|
|
2101
|
+
* Angular direkt am nativen Element vorgenommen wird (z. B. `el.setAttribute
|
|
2102
|
+
* ('href', …)` von Fremdcode), löst keinen Refresh aus — das gilt aber für jede
|
|
2103
|
+
* Angular-Bindung gleichermaßen und ist kein Sonderfall dieser Komponente.
|
|
2104
|
+
*
|
|
2105
|
+
* **Entscheidung 2 — Icon als projizierter Inhalt, OHNE dass das SVG eine eigene
|
|
2106
|
+
* Größenklasse tragen muss.** Wie bei `cds-stoerer` (siehe dessen Klassendoku) sind
|
|
2107
|
+
* die Icons auf den Beispielseiten wechselnde Heroicons, keine DS-Bereichsglyphen aus
|
|
2108
|
+
* der Registry `icons/cds-icons.ts` — Inhalt, nicht Chrom. Deshalb Projektion über
|
|
2109
|
+
* `<ng-content select="[cdsIcon]">` statt eines `icon`-Strings. Anders als beim Störer
|
|
2110
|
+
* ist `.ep-card-icon` hier aber ein echter CONTAINER: css/components.css:1310+1318
|
|
2111
|
+
* setzt Maße und Stroke über den Nachfahren-Selektor `.ep-card-icon svg{width:32px;
|
|
2112
|
+
* height:32px;stroke-width:var(--icon-stroke-sm)}`, nicht über eine Klasse auf dem
|
|
2113
|
+
* SVG selbst. Der Konsument liefert deshalb ein unverändertes `<svg cdsIcon
|
|
2114
|
+
* viewBox="…" aria-hidden="true" focusable="false">…</svg>` OHNE zusätzliche Klasse —
|
|
2115
|
+
* das Verhältnis Größe/Stroke kommt allein aus dem Container. Geprüft an
|
|
2116
|
+
* css/components.css:1298–1318 (Kommentar dort: „Farbe kommt aus der Komponente,
|
|
2117
|
+
* nicht aus … inline gesetzten style-Attributen“) und in der Story „Interaktiv“
|
|
2118
|
+
* gepinnt (Play-Funktion misst die gerenderte SVG-Breite).
|
|
2119
|
+
*
|
|
2120
|
+
* **`data-area` sitzt auf drei Elementen** (`.ep-card` (Host), `.ep-card-icon`,
|
|
2121
|
+
* `.ep-card-eyebrow`, wie im Mockup, `docs/index.html:4681–4686`): die CTA-Farbe
|
|
2122
|
+
* braucht dagegen KEIN eigenes `data-area`, sie kommt über den Nachfahren-Selektor
|
|
2123
|
+
* `.ep-card[data-area="…"] .ep-card-cta` (css/components.css:1336–1339) vom
|
|
2124
|
+
* `data-area` der Karte selbst.
|
|
2125
|
+
*
|
|
2126
|
+
* **Kein erfundener `aria-label`.** Der Pfeil-Suffix der CTA-Zeile ist reine visuelle
|
|
2127
|
+
* Affordanz (`docs/index.html:4686`: `<span aria-hidden="true">→</span>`) und steht
|
|
2128
|
+
* deshalb `aria-hidden`, unverändert zum Mockup — kein zusätzlicher, selbst
|
|
2129
|
+
* ausgedachter zugänglicher Name.
|
|
2130
|
+
*
|
|
2131
|
+
* **Entscheidung 3 — kein `cdsIconCards`-Raster.** `.ep-cards` ist ein reines
|
|
2132
|
+
* `display:grid` ohne Struktur oder Verhalten (dieselbe Begründung wie beim Verzicht
|
|
2133
|
+
* auf einen `layout-grid`-Wrapper, siehe `spec.md`). Konsumenten schreiben
|
|
2134
|
+
* `<div class="ep-cards">` von Hand (siehe Story „Im Raster“).
|
|
2135
|
+
*/
|
|
2136
|
+
declare class IconCardComponent {
|
|
2137
|
+
private readonly elementRef;
|
|
2138
|
+
/** Kartentitel (`.ep-card-title`). */
|
|
2139
|
+
readonly title: _angular_core.InputSignal<string>;
|
|
2140
|
+
/** Anreißer-/Beschreibungstext (`.ep-card-text`). */
|
|
2141
|
+
readonly text: _angular_core.InputSignal<string>;
|
|
2142
|
+
/** Kicker-Text oberhalb des Titels (`.ep-card-eyebrow`, leer = keine Eyebrow-Zeile). */
|
|
2143
|
+
readonly eyebrow: _angular_core.InputSignal<string>;
|
|
2144
|
+
/** Markenbereich → `data-area` auf Karte, Icon-Kachel und Eyebrow. */
|
|
2145
|
+
readonly area: _angular_core.InputSignal<CdsArea | undefined>;
|
|
2146
|
+
/** CTA-Zeile am Kartenfuß (`.ep-card-cta`, leer = keine CTA-Zeile). */
|
|
2147
|
+
readonly ctaLabel: _angular_core.InputSignal<string>;
|
|
2148
|
+
/**
|
|
2149
|
+
* Ob der Host ein `<a>` MIT `href` ist → schaltet `.ep-card-link` (Schatten/Hover,
|
|
2150
|
+
* `CONTRIBUTING.md` §4). Als Methode im Host-Binding aufgerufen (nicht einmalig in
|
|
2151
|
+
* ein Feld geschrieben), damit ein zur Laufzeit gesetztes/entferntes `href` erkannt
|
|
2152
|
+
* wird — Begründung und Grenzen in der Klassendoku oben.
|
|
2153
|
+
*
|
|
2154
|
+
* @internal
|
|
2155
|
+
*/
|
|
2156
|
+
protected isLink(): boolean;
|
|
2157
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<IconCardComponent, never>;
|
|
2158
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<IconCardComponent, "a[cdsIconCard], div[cdsIconCard]", never, { "title": { "alias": "title"; "required": true; "isSignal": true; }; "text": { "alias": "text"; "required": true; "isSignal": true; }; "eyebrow": { "alias": "eyebrow"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; "ctaLabel": { "alias": "ctaLabel"; "required": false; "isSignal": true; }; }, {}, never, ["[cdsIcon]"], true, never>;
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
/**
|
|
2162
|
+
* Feature (`[cdsFeature]`) — Wrapper um `.ep-feature` aus css/components.css
|
|
2163
|
+
* (css/components.css:1363–1379): die flache Feature-Zeile ohne Box und ohne
|
|
2164
|
+
* Schatten (Icon-Kachel links, Titel/Text rechts) — die „Offene Feature-Liste“ der
|
|
2165
|
+
* Beispielseiten (Doku-Sektion `docs/index.html:4882` ff., Beispielseiten u. a.
|
|
2166
|
+
* `docs/index.html:10405–10415`, `11582–11588`; 40 Vorkommen als
|
|
2167
|
+
* `class="ep-feature col-4"` im `.layout-grid`).
|
|
2168
|
+
*
|
|
2169
|
+
* **Attributselektor, kein eigenes Element (ADR-0008).** Das Mockup setzt die
|
|
2170
|
+
* Spaltenklasse am selben Element wie `.ep-feature` (`class="ep-feature col-4"`).
|
|
2171
|
+
* Das Bauteil ist damit selbst Grid-Kind UND Ziel einer Layout-Klasse des
|
|
2172
|
+
* Konsumenten — exakt das Kriterium aus
|
|
2173
|
+
* [ADR-0008](../../../../../../docs/adr/0008-selektortyp-der-wrapper-komponenten.md).
|
|
2174
|
+
* Ein eigenes `<cds-feature>`-Element mit innerem `<div class="ep-feature">` würde
|
|
2175
|
+
* `.layout-grid>[class*="col-"]` (css/base.css:214) brechen: der `col-*`-Selektor
|
|
2176
|
+
* träfe den unsichtbaren Host, nicht `.ep-feature`, das `.layout-grid` gibt seinem
|
|
2177
|
+
* Kind (dem Host) dann zwar per `align-items:stretch` (Grid-Default) die volle
|
|
2178
|
+
* Zeilenhöhe, aber `.ep-feature` darunter bliebe auf Inhaltshöhe — derselbe Fehler,
|
|
2179
|
+
* den ADR-0008 Fall 1 für `.ep-card`/`.ep-cards` gemessen hat (174px/270px statt
|
|
2180
|
+
* 306px/306px). Die Komponente hängt sich deshalb als Attribut an ein vom
|
|
2181
|
+
* Konsumenten geschriebenes `<div>` (`<div cdsFeature class="col-4">`), analog zu
|
|
2182
|
+
* `cds-icon-card` (siehe dessen Klassendoku). Gemessen für dieses Bauteil in der
|
|
2183
|
+
* Story „Dreispalter“ (Play-Funktion): drei `.ep-feature` mit stark
|
|
2184
|
+
* unterschiedlich langem Text als `col-4`-Geschwister in einem `.layout-grid`
|
|
2185
|
+
* ergeben identische Höhen und deckungsgleiche Unterkanten (Messwerte in der
|
|
2186
|
+
* Story-Doku dort).
|
|
2187
|
+
*
|
|
2188
|
+
* **Icon-Kontrakt: `.ep-feature-icon` ist ein Container, keine Klasse auf dem
|
|
2189
|
+
* SVG — UND reicht zusätzlich die Farbe durch, anders als `.ep-card-icon`.** Wie
|
|
2190
|
+
* `.ep-card-icon` (siehe `icon-card.component.ts`, Entscheidung 2) setzt
|
|
2191
|
+
* css/components.css:1369 Maße und Stroke über den Nachfahren-Selektor
|
|
2192
|
+
* `.ep-feature-icon svg{width:26px;height:26px;stroke-width:var(--icon-stroke-md)}`
|
|
2193
|
+
* — NICHT über eine Klasse auf dem SVG selbst, anders als `.stoerer-icon`
|
|
2194
|
+
* (`stoerer.component.ts`), das direkt auf dem projizierten `<svg>` sitzt. Der
|
|
2195
|
+
* Konsument liefert deshalb ein unverändertes
|
|
2196
|
+
* `<svg cdsIcon viewBox="…" aria-hidden="true" focusable="false">…</svg>` OHNE
|
|
2197
|
+
* zusätzliche Größenklasse, projiziert über `<ng-content select="[cdsIcon]">`
|
|
2198
|
+
* (Kontrakt in der Story „Interaktiv“ per `getBoundingClientRect()` gepinnt).
|
|
2199
|
+
* Anders als `.ep-card-icon` (nur `background`, kein `color`) setzt
|
|
2200
|
+
* `.ep-feature-icon` UND ihre vier `[data-area]`-Varianten (css/components.css:
|
|
2201
|
+
* 1367–1372) zusätzlich `color` auf dem Container selbst. Das Mockup nutzt das
|
|
2202
|
+
* aktiv aus: die projizierten SVGs dort (`docs/index.html:10406` u. a.) tragen
|
|
2203
|
+
* `stroke="currentColor"` statt eines hart codierten Bereichstons — die Farbe
|
|
2204
|
+
* kommt allein aus dem Container, ein Icon-Autor muss den Bereich der Kachel gar
|
|
2205
|
+
* nicht kennen. Bei `cds-icon-card` ist das nicht möglich (kein `color` am
|
|
2206
|
+
* Container), dort trägt jedes Icon seinen Bereichston selbst im `stroke`-Attribut.
|
|
2207
|
+
* Story „Interaktiv“ demonstriert das mit `stroke="currentColor"` und prüft die
|
|
2208
|
+
* geerbte Farbe per `getComputedStyle()`. Die Icon-Kachel selbst ist im Mockup
|
|
2209
|
+
* (`docs/index.html:4883` u. a.) ein `<span aria-hidden="true">`, kein `<div>` —
|
|
2210
|
+
* rein dekorativ, das SVG trägt bereits sein eigenes `aria-hidden`; die Komponente
|
|
2211
|
+
* übernimmt beides unverändert.
|
|
2212
|
+
*
|
|
2213
|
+
* **CTA: `<a>` mit `href`, sonst `<span>` — nie ein `<a>` ohne `href`.**
|
|
2214
|
+
* css/components.css:1376 zielt mit `.ep-feature-body>.card-cta-link` auf den
|
|
2215
|
+
* direkten Nachfahren; Angulars `@if`/`@else` fügt dafür kein Wrapper-Element ein
|
|
2216
|
+
* (kompiliert zu Kommentar-Ankern, kein Element), der Kindselektor bleibt also in
|
|
2217
|
+
* beiden Zweigen erhalten. Ausgezählt in `docs/index.html`: alle 13 Vorkommen von
|
|
2218
|
+
* `.card-cta-link` innerhalb von `.ep-feature-body` sind entweder ein `<a
|
|
2219
|
+
* href="…">` (12×) oder ein `<span>` (1×, `docs/index.html:13216`,
|
|
2220
|
+
* „Landingpage folgt“ — Platzhalter für ein Ziel, das noch fehlt). Ein `<a>` OHNE
|
|
2221
|
+
* `href` kommt kein einziges Mal vor — dieselbe Falle, die `cds-icon-card` über
|
|
2222
|
+
* `isLink()` schließt (ein Link-Tag ohne `href` ist weder fokussierbar noch hat
|
|
2223
|
+
* es eine Link-Rolle, sieht mit der `.card-cta-link`-Optik aber trotzdem wie ein
|
|
2224
|
+
* bedienbares Element aus). Hier entscheidet sich das nicht am Host-Tag (der ist
|
|
2225
|
+
* bei `cdsFeature` immer `<div>`), sondern strukturell im eigenen Template:
|
|
2226
|
+
* `ctaHref` gesetzt → `<a class="card-cta-link" [href]="ctaHref()">`, leer →
|
|
2227
|
+
* `<span class="card-cta-link">` mit identischer Optik und identischem Pfeil,
|
|
2228
|
+
* aber ohne Link-Rolle (siehe Story „Ohne Href“). Ein `[attr.href]`-Nullwert wäre
|
|
2229
|
+
* dieselbe Krücke gewesen wie ein `<a>` ohne `href` zu tolerieren; der
|
|
2230
|
+
* `@if`/`@else`-Zweig macht den Fall stattdessen unmöglich, statt ihn nur
|
|
2231
|
+
* abzufedern. `data-area` sitzt auf beiden Zweigen, weil `.card-cta-link[data-area]`
|
|
2232
|
+
* (css/components.css:1363–1366) ein direkter Attributselektor ist, keine
|
|
2233
|
+
* Nachfahren-Regel wie `.ep-card[data-area] .ep-card-cta` bei der Icon-Karte —
|
|
2234
|
+
* ohne eigenes `data-area` bliebe der Link/Span auf der CSS-Vorgabefarbe, auch bei
|
|
2235
|
+
* gesetztem `area`.
|
|
2236
|
+
*
|
|
2237
|
+
* **`ctaAriaLabel` — der zugängliche Name ist im Mockup die Regel, nicht die
|
|
2238
|
+
* Ausnahme.** Erster Anlauf dieser Komponente hatte keinen solchen Input, mit der
|
|
2239
|
+
* (falschen) Annahme, das Mockup verzichte meist auf einen zusätzlichen
|
|
2240
|
+
* `aria-label`. Ausgezählt: von den 12 `<a class="card-cta-link">` in
|
|
2241
|
+
* `.ep-feature-body` tragen 11 einen `aria-label`, nur einer nicht
|
|
2242
|
+
* („Contentletter abonnieren“, `docs/index.html:15203` — der sichtbare Text ist
|
|
2243
|
+
* dort bereits eindeutig). Grund: nur 3 verschiedene sichtbare Texte verteilen
|
|
2244
|
+
* sich auf diese 12 Links, „Zur Landingpage“ allein zehnmal
|
|
2245
|
+
* (`docs/index.html:11587` u. a.) — eine Screenreader-Linkliste hörte sonst
|
|
2246
|
+
* zehnmal denselben Namen ohne Unterscheidung. Das Mockup disambiguiert
|
|
2247
|
+
* durchgängig mit der Form `<sichtbarer Text>: <Ziel>`
|
|
2248
|
+
* (`aria-label="Zur Landingpage: KI Kickstart Workshops"`). `ctaAriaLabel` ist
|
|
2249
|
+
* deshalb Beiwerk (Default `''`): leer bleibt der sichtbare `ctaLabel`-Text der
|
|
2250
|
+
* zugängliche Name (passt für einen eindeutigen CTA-Text wie „Contentletter
|
|
2251
|
+
* abonnieren“), gesetzt überschreibt er ihn — Konsumenten mit einer Liste
|
|
2252
|
+
* gleichlautender CTA-Texte (wie den zehn „Zur Landingpage“ im Mockup) setzen ihn
|
|
2253
|
+
* pro Instanz auf `<ctaLabel>: <Ziel>`. Wirkt nur am `<a>`-Zweig: `aria-label` an
|
|
2254
|
+
* einem `<span>` ohne Rolle hat keinen verlässlichen Effekt im
|
|
2255
|
+
* Accessibility-Baum, und der `<span>`-Zweig ist ohnehin nicht interaktiv.
|
|
2256
|
+
*/
|
|
2257
|
+
declare class FeatureComponent {
|
|
2258
|
+
/** Titel (`.ep-feature-title`). */
|
|
2259
|
+
readonly title: _angular_core.InputSignal<string>;
|
|
2260
|
+
/** Beschreibungstext (`.ep-feature-text`). */
|
|
2261
|
+
readonly text: _angular_core.InputSignal<string>;
|
|
2262
|
+
/** Markenbereich → `data-area` auf Icon-Kachel und CTA-Link. */
|
|
2263
|
+
readonly area: _angular_core.InputSignal<CdsArea | undefined>;
|
|
2264
|
+
/** CTA-Linktext (`.card-cta-link` als direktes Kind von `.ep-feature-body`, leer = kein CTA). */
|
|
2265
|
+
readonly ctaLabel: _angular_core.InputSignal<string>;
|
|
2266
|
+
/** CTA-Linkziel; gesetzt → `<a href>`, leer → `<span>` ohne Link-Rolle (siehe Klassendoku). */
|
|
2267
|
+
readonly ctaHref: _angular_core.InputSignal<string>;
|
|
2268
|
+
/**
|
|
2269
|
+
* Zugänglicher Name des CTA-Links, überschreibt den sichtbaren `ctaLabel`-Text.
|
|
2270
|
+
* Nötig, wenn derselbe sichtbare CTA-Text mehrfach auf einer Seite steht (siehe
|
|
2271
|
+
* Klassendoku); Form aus dem Mockup: `<ctaLabel>: <Ziel>`. Leer (Default) = kein
|
|
2272
|
+
* `aria-label`, der sichtbare `ctaLabel`-Text bleibt der zugängliche Name. Wirkt
|
|
2273
|
+
* nur, wenn `ctaHref` gesetzt ist (nur der `<a>`-Zweig hat eine Link-Rolle).
|
|
2274
|
+
*/
|
|
2275
|
+
readonly ctaAriaLabel: _angular_core.InputSignal<string>;
|
|
2276
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FeatureComponent, never>;
|
|
2277
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FeatureComponent, "div[cdsFeature]", never, { "title": { "alias": "title"; "required": true; "isSignal": true; }; "text": { "alias": "text"; "required": true; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; "ctaLabel": { "alias": "ctaLabel"; "required": false; "isSignal": true; }; "ctaHref": { "alias": "ctaHref"; "required": false; "isSignal": true; }; "ctaAriaLabel": { "alias": "ctaAriaLabel"; "required": false; "isSignal": true; }; }, {}, never, ["[cdsIcon]"], true, never>;
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
/**
|
|
2281
|
+
* CtaBand (`[cdsCtaBand]`) — Wrapper um `.ep-cta-band` aus css/components.css
|
|
2282
|
+
* (css/components.css:1456, Kopf-Duo `.ep-cta-h2`/`.ep-cta-sub` ebenda): das
|
|
2283
|
+
* bereichsgefärbte Page-End-CTA-Band, das laut Doku-Site (`docs/index.html:6237`,
|
|
2284
|
+
* Anker `gt-cta-band`) am Ende jeder Customer-Page direkt vor dem Footer steht — die
|
|
2285
|
+
* „letzte Einladung“, wenn die Nutzerin die Seite durchgelesen hat. Ausgezählt: 22
|
|
2286
|
+
* Vorkommen auf den Beispielseiten, ausnahmslos als `<div class="ep-cta-band"
|
|
2287
|
+
* style="background:…">` mit genau einer Aktion.
|
|
2288
|
+
*
|
|
2289
|
+
* **Entscheidung 1 — Attributselektor, kein eigenes Element (ADR-0008, derselbe
|
|
2290
|
+
* „Fläche am Host“-Fall wie `cds-section`).** `css/components.css` trägt für
|
|
2291
|
+
* `.ep-cta-band` KEIN `[data-area]` (per Grep geprüft, keine einzige Regel dieser
|
|
2292
|
+
* Form existiert). Die Bandfläche kommt in allen 22 Vorkommen als Inline-Style
|
|
2293
|
+
* DIREKT am `.ep-cta-band`-Element (`style="background:var(--co-700);color:#fff"`,
|
|
2294
|
+
* docs/index.html:6304 u. a.), dem Bereich der Page folgend, nie aus einem
|
|
2295
|
+
* `data-area`-Attribut. Ein Element-Selektor würde exakt den in ADR-0008 „Fall 2“
|
|
2296
|
+
* gemessenen Fehler wiederholen: der `<cds-cta-band>`-Host ist ein unbekanntes
|
|
2297
|
+
* Custom Element (`display:inline`), sein einziges Kind würde als Block
|
|
2298
|
+
* herausgebrochen, ein am Host gesetzter Inline-`background` hätte keine eigene Box
|
|
2299
|
+
* zum Malen. Die Komponente hängt sich deshalb als Attribut an ein vom Konsumenten
|
|
2300
|
+
* geschriebenes `<div>` (`<div cdsCtaBand style="background:…">`) — das Element, das
|
|
2301
|
+
* den Style trägt, und `.ep-cta-band` sind derselbe Knoten. Nur `<div>`, kein
|
|
2302
|
+
* `<section>`-Zwilling wie bei `cds-section`: alle 22 Vorkommen benutzen
|
|
2303
|
+
* ausnahmslos ein `<div>`, keines variiert das Tag.
|
|
2304
|
+
*
|
|
2305
|
+
* **Entscheidung 2 — `area` färbt nur die Aktion, nicht das Band.** Aus demselben
|
|
2306
|
+
* Grund wie bei `cds-section` (siehe dessen Klassendoku, Entscheidung 2) bleibt die
|
|
2307
|
+
* Bandfläche Sache des Konsumenten: welche Bereichsfarbe ein Band trägt, ist eine
|
|
2308
|
+
* Entscheidung der Seite (Bereichszugehörigkeit der Customer-Page), keine
|
|
2309
|
+
* Bauteil-Eigenschaft — und das CSS bietet an dieser Klasse ohnehin keinen
|
|
2310
|
+
* `[data-area]`-Haken an (siehe Entscheidung 1). `area` steuert deshalb
|
|
2311
|
+
* ausschließlich die Button-Farbklasse (`.btn-{area}`), nicht die Füllung des Bands.
|
|
2312
|
+
*
|
|
2313
|
+
* **Entscheidung 3 — `.btn-on-band` + `.btn-filled` direkt komponiert, nicht über
|
|
2314
|
+
* `cds-button` projiziert.** `cds-button` KENNT den Modifier
|
|
2315
|
+
* (`variant="filled-on-band"`, geprüft über `npx storybook tools docs show --id
|
|
2316
|
+
* komponenten-buttons-button` — die Story „Auf Bereichs-Band“ existiert dort
|
|
2317
|
+
* bereits). Trotzdem baut diese Komponente die `.btn`-Klassen direkt zusammen
|
|
2318
|
+
* (Präzedenzfall `download-cta.component.ts`), aus einem härteren Grund als dort:
|
|
2319
|
+
* `cds-button` rendert IMMER ein `<button>` — sein Input-Vertrag
|
|
2320
|
+
* (`ButtonComponentInputs`) kennt kein `href` — und kann die vom Ticket geforderte
|
|
2321
|
+
* Verzweigung „`primaryHref` gesetzt → `<a>`“ strukturell gar nicht erfüllen. Ein
|
|
2322
|
+
* projiziertes `<cds-button>` brächte hier also kein Custom-Element-Layoutproblem
|
|
2323
|
+
* (kein Kindselektor an `.ep-cta-band` hängt von der DOM-Tiefe ab), sondern schlicht
|
|
2324
|
+
* die falsche Fähigkeit.
|
|
2325
|
+
*
|
|
2326
|
+
* **Entscheidung 4 — bewusst nur EINE Aktion, kein `secondaryLabel`.** Eine frühere
|
|
2327
|
+
* Fassung bot `secondaryLabel`/`secondaryHref` an, ungeprüft gegen das Mockup. Beide
|
|
2328
|
+
* Belege dagegen: erstens zeigt KEINES der 22 `.ep-cta-band`-Vorkommen in
|
|
2329
|
+
* `docs/index.html` eine zweite Aktion — der Zwei-Aktionen-Fall hat keine Vorlage.
|
|
2330
|
+
* Zweitens (der schwerere Grund) lässt sich eine zweite, optisch zurückhaltendere
|
|
2331
|
+
* Aktion mit den vorhandenen Klassen gar nicht bauen: `.btn-{area}.btn-on-band{color:
|
|
2332
|
+
* …}` (css/components.css:47–51) überschreibt die Textfarbe JEDER Variante auf den
|
|
2333
|
+
* Bereichston — genau den Ton, den die Doku durchgängig als Bandhintergrund nennt.
|
|
2334
|
+
* Nur `.btn-filled` tauscht zusätzlich die Füllung gegen Weiß; `.btn-outlined`,
|
|
2335
|
+
* `.btn-tonal` und `.btn-text` blieben transparent und zeigten Text in derselben
|
|
2336
|
+
* Farbe wie das Band darunter — unsichtbar. Eine zweite Aktion hätte deshalb
|
|
2337
|
+
* zwangsläufig dieselbe Optik wie die erste getragen, keine Hierarchie, nur zwei
|
|
2338
|
+
* gleich gewichtete Buttons. Das ist eine CSS-Lücke (kein kontrastsicherer
|
|
2339
|
+
* sekundärer On-Band-Stil), festgehalten in
|
|
2340
|
+
* `.scratch/angular-seitenbausteine/issues/16-css-luecke-zweite-aktion-auf-band.md`
|
|
2341
|
+
* — gemeldet, nicht in diesem Wrapper improvisiert. Solange sie besteht, trägt
|
|
2342
|
+
* `cds-cta-band` nur eine Aktion; ein Konsument mit echtem Bedarf für eine zweite
|
|
2343
|
+
* schreibt sie außerhalb der Komponente ins Band hinein (`<ng-content>` gibt es
|
|
2344
|
+
* hier bewusst nicht, um genau das nicht als unterstützten Pfad zu suggerieren).
|
|
2345
|
+
* Ohne zweite Aktion braucht es auch keinen eigenen Container: die einzelne Aktion
|
|
2346
|
+
* zentriert sich über das ererbte `.ep-cta-band{text-align:center}` von selbst,
|
|
2347
|
+
* genau wie in allen 22 Mockup-Vorkommen — ein zusätzliches Flex-Layout im Wrapper
|
|
2348
|
+
* hätte eine Darstellung erfunden, die die CSS-Schicht so nicht kennt (ADR-0001;
|
|
2349
|
+
* die einzige existierende Deklaration dieser Form ist `.ep-hero-ctas`,
|
|
2350
|
+
* css/components.css:1222, eine andere Klasse für einen anderen Bauteiltyp).
|
|
2351
|
+
*
|
|
2352
|
+
* **Kein `aria-label` auf der Aktion.** Anders als bei `cds-feature`
|
|
2353
|
+
* (`ctaAriaLabel`, 11 von 12 Vorkommen mit `aria-label`) zeigt keines der 22
|
|
2354
|
+
* Band-Vorkommen einen `aria-label` auf seiner Aktion — ausgezählt, nicht
|
|
2355
|
+
* geschätzt. Die sichtbaren Texte sind durchgängig konkrete Verben mit Ziel
|
|
2356
|
+
* („Termin buchen“, „Gespräch anfragen“, „Offene Stellen ansehen“ …) und stehen je
|
|
2357
|
+
* Seite nur einmal; anders als bei einer Karten-Liste braucht es hier keine
|
|
2358
|
+
* Disambiguierung. Kein entsprechender Input.
|
|
2359
|
+
*/
|
|
2360
|
+
declare class CtaBandComponent {
|
|
2361
|
+
/** Headline (`.ep-cta-h2`) — das konkrete Angebot, keine Marketing-Floskel. */
|
|
2362
|
+
readonly heading: _angular_core.InputSignal<string>;
|
|
2363
|
+
/** Beschriftung der (einzigen) Aktion. */
|
|
2364
|
+
readonly primaryLabel: _angular_core.InputSignal<string>;
|
|
2365
|
+
/** Unterzeile (`.ep-cta-sub`, leer = keine Unterzeile). */
|
|
2366
|
+
readonly sub: _angular_core.InputSignal<string>;
|
|
2367
|
+
/** Markenbereich → `.btn-{area}` auf der Aktion (nicht die Bandfläche, siehe Klassendoku). */
|
|
2368
|
+
readonly area: _angular_core.InputSignal<CdsArea>;
|
|
2369
|
+
/** Ziel der Aktion; gesetzt → `<a href>`, leer → `<button>` mit `primaryClick`. */
|
|
2370
|
+
readonly primaryHref: _angular_core.InputSignal<string>;
|
|
2371
|
+
/** Klick auf die Aktion (feuert nur, wenn `primaryHref` leer ist). */
|
|
2372
|
+
readonly primaryClick: _angular_core.OutputEmitterRef<MouseEvent>;
|
|
2373
|
+
/** @internal */
|
|
2374
|
+
protected readonly actionClasses: _angular_core.Signal<string>;
|
|
2375
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<CtaBandComponent, never>;
|
|
2376
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<CtaBandComponent, "div[cdsCtaBand]", never, { "heading": { "alias": "heading"; "required": true; "isSignal": true; }; "primaryLabel": { "alias": "primaryLabel"; "required": true; "isSignal": true; }; "sub": { "alias": "sub"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; "primaryHref": { "alias": "primaryHref"; "required": false; "isSignal": true; }; }, { "primaryClick": "primaryClick"; }, never, never, true, never>;
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
/**
|
|
2380
|
+
* Tier (`cds-tier`) — Wrapper um `.ep-tier` aus css/components.css
|
|
2381
|
+
* (css/components.css:1381–1384): der Stufen-Trenner (Label + Haarlinie), der eine
|
|
2382
|
+
* Offene Feature-Liste in Pakete gliedert (z. B. „In jedem Paket enthalten“ /
|
|
2383
|
+
* „Zusätzlich mit Pro“, Doku-Sektion `docs/index.html:4880`, Beispielseiten
|
|
2384
|
+
* `docs/index.html:11855–11857`, `11883–11885`, `12576–12578`).
|
|
2385
|
+
*
|
|
2386
|
+
* **Element-Selektor, kein Attribut (ADR-0008-Standardfall).** Ausgezählt: alle 4
|
|
2387
|
+
* `.ep-tier`-Vorkommen in `docs/index.html` stehen als eigenständiger `<div>` VOR
|
|
2388
|
+
* einem `.layout-grid` oder einer Karten-Reihe, nie als deren Kind — keines trägt
|
|
2389
|
+
* eine `col-*`-Spaltenklasse, keines liegt in einem Grid/Flex, das seine Kinder
|
|
2390
|
+
* dehnt. Das Tag variiert ebenfalls nicht (immer `<div>`). Keines der drei
|
|
2391
|
+
* ADR-0008-Kriterien (Grid-/Flex-Kind, Ziel einer Layout-Klasse, wechselndes Tag)
|
|
2392
|
+
* trifft zu, der Element-Selektor bleibt deshalb der Standardfall. `.ep-tier{
|
|
2393
|
+
* display:flex}` sitzt direkt auf dem Host (`host: { class: 'ep-tier' }`), kein
|
|
2394
|
+
* inneres Wrapper-Element darunter — anders als bei `cds-section` (ADR-0008 Fall 2)
|
|
2395
|
+
* ist hier nichts von außen auf eine bestimmte DOM-Tiefe angewiesen: kein
|
|
2396
|
+
* Nachfahren-Selektor zielt auf `.ep-tier`, keine Fläche hängt am Host.
|
|
2397
|
+
*
|
|
2398
|
+
* **`area` ist auf `'ki'` typisiert, nicht auf `CdsArea`.** `css/components.css:1383`
|
|
2399
|
+
* kennt nur die eine Regel `.ep-tier-label[data-area="ki"]` (Dark-Override
|
|
2400
|
+
* `css/dark-mode.css:476`) — für `co`/`es`/`wo` existiert keine einzige
|
|
2401
|
+
* `[data-area]`-Regel auf `.ep-tier-label`, per Grep über `css/*.css` geprüft.
|
|
2402
|
+
* Ausgezählt in `docs/index.html`: von 4 `.ep-tier-label`-Vorkommen tragen 3
|
|
2403
|
+
* `data-area="ki"` (4880, 11884, 12577), eines trägt gar kein `data-area` (11856),
|
|
2404
|
+
* keines der Werte `co`/`es`/`wo` kommt vor. Ein `area`-Input vom Typ `CdsArea`
|
|
2405
|
+
* würde für drei von vier gültigen Werten ein `data-area`-Attribut schreiben, das
|
|
2406
|
+
* im CSS folgenlos bleibt — die Falle, die genau dann entsteht, wenn eine Eingabe
|
|
2407
|
+
* mehr verspricht, als die Stilschicht einlöst. Der Typ ist deshalb auf den einen
|
|
2408
|
+
* Wert eingeschränkt, den das CSS tatsächlich kennt; der Compiler verhindert damit
|
|
2409
|
+
* strukturell, dass jemand `co`/`es`/`wo` setzt und ein wirkungsloses Attribut
|
|
2410
|
+
* bekommt, statt es nur im JSDoc zu behaupten.
|
|
2411
|
+
*/
|
|
2412
|
+
declare class TierComponent {
|
|
2413
|
+
/** Beschriftung des Trenners (`.ep-tier-label`), z. B. „In jedem Paket enthalten“. */
|
|
2414
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
2415
|
+
/**
|
|
2416
|
+
* Einzige vom CSS unterstützte Tönung → `data-area="ki"` auf `.ep-tier-label`.
|
|
2417
|
+
* Ungesetzt (Default) bleibt das Label neutral (`--tx-secondary`), siehe Klassendoku.
|
|
2418
|
+
*/
|
|
2419
|
+
readonly area: _angular_core.InputSignal<"ki" | undefined>;
|
|
2420
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TierComponent, never>;
|
|
2421
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<TierComponent, "cds-tier", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
2422
|
+
}
|
|
2423
|
+
|
|
2424
|
+
/** Ein Fakten-Paar der Angebots-Rahmendaten (`<dt>`/`<dd>`). */
|
|
2425
|
+
interface CdsFactsItem {
|
|
2426
|
+
/** Label (`<dt>`), z. B. „Dauer“. */
|
|
2427
|
+
term: string;
|
|
2428
|
+
/** Wert (`<dd>`), z. B. „2 Tage, 9–17 Uhr“. */
|
|
2429
|
+
value: string;
|
|
2430
|
+
}
|
|
2431
|
+
/**
|
|
2432
|
+
* Facts (`cds-facts`) — Wrapper um `.ep-facts` aus css/components.css
|
|
2433
|
+
* (css/components.css:1261–1272): die Definitionsliste für die harten Eckdaten
|
|
2434
|
+
* eines Angebots (Termin, Dauer, Ort, Preis), einspaltig mit Haarlinie zwischen
|
|
2435
|
+
* den Paaren oder als `.is-grid` zweispaltig für Kästen, die neben Inhalt stehen.
|
|
2436
|
+
* Bewusst ohne eigenen Rahmen und ohne Fläche, sie zieht in einen vorhandenen
|
|
2437
|
+
* Container ein (Angebots-Box, Sticky-Sidebar) — Doku-Sektion
|
|
2438
|
+
* `docs/index.html:9069` (`gt-seminar-fakten`), Beispielseiten `docs/index.html:
|
|
2439
|
+
* 9076–9082`, `13718–13726`, `16293–16300`.
|
|
2440
|
+
*
|
|
2441
|
+
* **Element-Selektor, kein Attribut (ADR-0008-Standardfall).** Ausgezählt: keines
|
|
2442
|
+
* der 3 `.ep-facts`-Vorkommen in `docs/index.html` sitzt in einem `.layout-grid`
|
|
2443
|
+
* mit `col-*`, keines wird von einem Grid-/Flex-Elternteil in der Höhe gedehnt
|
|
2444
|
+
* (der Flex-Fall in Zeile 16290 ist `flex-direction:column`, dort füllen Block-
|
|
2445
|
+
* Elemente die Breite ihres Containers unabhängig von einer zusätzlichen
|
|
2446
|
+
* Wrapper-Ebene — anders als bei `.ep-card`/ADR-0008 Fall 1 geht es dort um Höhe,
|
|
2447
|
+
* die durch Block-Layout NICHT automatisch nach unten durchgereicht wird). Das
|
|
2448
|
+
* Tag variiert nicht (immer `<dl>`). Analog zu `cds-faq` (`faq.component.ts`, das
|
|
2449
|
+
* intern `<details>` in einem `<div class="ep-faq">` rendert) trägt hier die
|
|
2450
|
+
* INNERE `<dl class="ep-facts">` die CSS-Klasse, nicht der `<cds-facts>`-Host: die
|
|
2451
|
+
* Semantik einer Definitionsliste hängt am `<dl>`-Tag selbst, ein Custom-Element
|
|
2452
|
+
* kann dieses Tag nicht annehmen. Da `.ep-facts > div + div` ausschließlich
|
|
2453
|
+
* Nachfahren INNERHALB der eigenen Vorlage anspricht, bricht eine zusätzliche
|
|
2454
|
+
* Host-Ebene darüber nichts — anders als bei `cds-section` (Fall 2) hängt hier
|
|
2455
|
+
* keine von außen gesetzte Fläche oder ein von außen adressierter Nachfahre an
|
|
2456
|
+
* dieser Tiefe.
|
|
2457
|
+
*
|
|
2458
|
+
* **`<dl>`/`<div>`/`<dt>`/`<dd>` exakt wie vom CSS erwartet.** `.ep-facts > div +
|
|
2459
|
+
* div` (css/components.css:1262) setzt Haarlinie und Abstand ab dem ZWEITEN Paar;
|
|
2460
|
+
* ohne das umschließende `<div>` pro Paar griffe der Selektor nicht. Jedes
|
|
2461
|
+
* `items()`-Element wird deshalb zu genau einem `<div><dt>…</dt><dd>…</dd></div>`.
|
|
2462
|
+
*
|
|
2463
|
+
* **`area` färbt `<dt>` über `.t-{area}`, nicht über `data-area`.** Anders als bei
|
|
2464
|
+
* `cds-feature`/`cds-icon-card` kennt `.ep-facts` selbst kein `[data-area]`; der
|
|
2465
|
+
* CSS-Kommentar zu dieser Klasse (`css/components.css:1259`) benennt
|
|
2466
|
+
* stattdessen ausdrücklich „Bereichston der Labels über `.t-XX` im Markup
|
|
2467
|
+
* (dark-safe), nie als Inline-Farbe“ als sanktionierten Weg. Ausgezählt: alle 3
|
|
2468
|
+
* `.ep-facts`-Vorkommen in `docs/index.html` setzen `.t-wo` auf JEDEM `<dt>`
|
|
2469
|
+
* derselben Liste (nie gemischt, nie eine andere Bereichsfarbe) — ein optionaler
|
|
2470
|
+
* `area`-Input, der `.t-{area}` einheitlich auf alle `<dt>` der Instanz anwendet,
|
|
2471
|
+
* bildet dieses Muster nach, ohne einen Farbwert zu erfinden (`.t-XX` ist eine
|
|
2472
|
+
* bestehende, bereits kontrast- und dark-mode-geprüfte Utility, `css/components.css
|
|
2473
|
+
* :638–642`). Ungesetzt (Default) bleibt `<dt>` ohne zusätzliche Klasse, erbt also
|
|
2474
|
+
* die umgebende Textfarbe — das deckt Kontexte ohne Bereichsbindung ab.
|
|
2475
|
+
*
|
|
2476
|
+
* **Über die Ticket-API hinaus:** `area` stand nicht in der ursprünglichen
|
|
2477
|
+
* API-Skizze von Ticket 08. Ergänzt, weil ohne ihn KEINES der 3 realen
|
|
2478
|
+
* `.ep-facts`-Vorkommen originalgetreu nachgebaut werden könnte — anders als bei
|
|
2479
|
+
* `cds-tier`s `area` (dort deckt die Einschränkung eine tatsächliche CSS-Lücke
|
|
2480
|
+
* ab) fehlt hier keine CSS-Regel, nur ein API-Hook auf eine bereits vollständige
|
|
2481
|
+
* Klasse. Siehe Ticket-Bericht für die Abwägung.
|
|
2482
|
+
*/
|
|
2483
|
+
declare class FactsComponent {
|
|
2484
|
+
/** Fakten-Paare (`<dt>`/`<dd>`), in Darstellungsreihenfolge. */
|
|
2485
|
+
readonly items: _angular_core.InputSignal<CdsFactsItem[]>;
|
|
2486
|
+
/** Zweispaltige Rasterung ohne Haarlinien (`.is-grid`) statt der einspaltigen Grundform. */
|
|
2487
|
+
readonly grid: _angular_core.InputSignal<boolean>;
|
|
2488
|
+
/** Markenbereich → `.t-{area}` auf jedem `<dt>` (siehe Klassendoku). Ungesetzt = neutral. */
|
|
2489
|
+
readonly area: _angular_core.InputSignal<CdsArea | undefined>;
|
|
2490
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FactsComponent, never>;
|
|
2491
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FactsComponent, "cds-facts", never, { "items": { "alias": "items"; "required": true; "isSignal": true; }; "grid": { "alias": "grid"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2494
|
+
/**
|
|
2495
|
+
* Table (`cds-table`) — Wrapper um `.tbl`/`.tbl-wrap` aus css/components.css
|
|
2496
|
+
* (css/components.css:1462–1480): die Datentabelle der Doku-Site (`sec-table`,
|
|
2497
|
+
* Nav-Einträge „Standard“ und „Gestreift“). Ausgezählt: außerhalb von `sec-table`
|
|
2498
|
+
* selbst kommt `.tbl-wrap` nur zweimal vor, beide auf Beitragsseiten
|
|
2499
|
+
* (`docs/index.html:8622`, `15080`) — keine der 24 Beispielseiten außerhalb von
|
|
2500
|
+
* Wissensbeiträgen setzt aktuell eine Tabelle ein. Neuntes Ticket der
|
|
2501
|
+
* Seitenbausteine-Serie.
|
|
2502
|
+
*
|
|
2503
|
+
* **Kein Daten-Input.** Eine `columns`/`rows`-API würde Zellinhalte auf Strings
|
|
2504
|
+
* festlegen; die Beispielseiten setzen darin Badges, Links und `data-num`
|
|
2505
|
+
* (`docs/index.html:5076–5177`). Der Konsument projiziert `<thead>`/`<tbody>`/
|
|
2506
|
+
* `<tfoot>` unverändert per `<ng-content>`, die Komponente liefert nur die
|
|
2507
|
+
* Hülle: `.tbl-wrap`, `.tbl`, `<caption>` und die Striped-Klasse.
|
|
2508
|
+
*
|
|
2509
|
+
* **`caption` ist Pflicht (`input.required<string>()`), abweichend von der
|
|
2510
|
+
* Ticket-Skizze (dort `caption? = ''`).** Die eigene Doku dieser Komponente
|
|
2511
|
+
* (`storybook-angular/src/docs/komponenten/tabelle.mdx:137`) nennt `<caption>`
|
|
2512
|
+
* ausdrücklich „Pflicht, Screenreader lesen den Titel vor, bevor die Zellen
|
|
2513
|
+
* vorgelesen werden“ und wiederholt das in den Dos (`tabelle.mdx:147`:
|
|
2514
|
+
* „`<caption>` und `scope`-Attribute immer setzen, auch bei einfachen
|
|
2515
|
+
* Tabellen“). Ausgezählt: alle 5 realen `.tbl`-Vorkommen in `docs/index.html`
|
|
2516
|
+
* haben eine `<caption>`, ausnahmslos. Ein optionaler Input mit Default `''`
|
|
2517
|
+
* hätte genau den Fall erlaubt, den die eigene Doku verbietet — nach
|
|
2518
|
+
* ADR-0007 §2 ist `caption` damit Inhalt, nicht Beiwerk: ohne sie ist eine
|
|
2519
|
+
* `cds-table` eine Tabelle, die die eigenen Barrierefreiheitsregeln bricht,
|
|
2520
|
+
* also sinnlos. Ein vergessenes Binding wirft jetzt `NG0950`, statt eine
|
|
2521
|
+
* unbeschriftete Tabelle still auszuliefern.
|
|
2522
|
+
*
|
|
2523
|
+
* **Element-Selektor, `.tbl-wrap`/`.tbl` sitzen im eigenen Template, nicht am
|
|
2524
|
+
* Host (ADR-0008-Standardfall, analog zu `cds-facts`/`cds-faq`).** Ausgezählt:
|
|
2525
|
+
* alle 5 `.tbl-wrap`-Vorkommen in `docs/index.html` (Zeilen 5061, 5116, 5236,
|
|
2526
|
+
* 8622, 15080). Keines ist selbst ein direktes Grid-/Flex-Kind — das einzige
|
|
2527
|
+
* Vorkommen in einem `.layout-grid` (Zeile 5236) sitzt eine Ebene tiefer in
|
|
2528
|
+
* einem `.col-8`, das seinerseits der Grid-Child ist; `.tbl-wrap` ist dort ein
|
|
2529
|
+
* gewöhnlicher Block-Nachfahre, dessen Breite vom umschließenden `.col-8`
|
|
2530
|
+
* kommt, nicht von `align-items:stretch` auf einer bestimmten DOM-Tiefe (kein
|
|
2531
|
+
* `.ep-card`-Fall). Keines trägt eine `col-*`-Klasse selbst, keines variiert
|
|
2532
|
+
* das Tag (immer `<div class="tbl-wrap">`). Ein `<cds-table>`-Host ohne eigene
|
|
2533
|
+
* Klasse und ohne eigenes `background` gerät deshalb nicht in den „Fläche am
|
|
2534
|
+
* Host“-Fall aus ADR-0008 Fall 2: die Komponente setzt nichts auf dem Host,
|
|
2535
|
+
* das eine eigene Box bräuchte, `.tbl-wrap` bleibt ein normaler Block-Nachfahre
|
|
2536
|
+
* unabhängig vom `display` des `<cds-table>`-Hosts (derselbe Grund, warum
|
|
2537
|
+
* `cds-facts`/`cds-faq` als Element-Selektor funktionieren).
|
|
2538
|
+
*
|
|
2539
|
+
* Eine Attributselektor-Variante direkt am `<table>` (`table[cdsTable]`, analog
|
|
2540
|
+
* zu `cdsSection`) wurde erwogen und verworfen: `<table>` ist als Tag ohnehin
|
|
2541
|
+
* fix (kein Tag-Wechsel wie bei `cdsIconCard`), aber `.tbl-wrap` bräuchte dann
|
|
2542
|
+
* ein ZWEITES Element, das der Konsument von Hand um die Tabelle schreiben
|
|
2543
|
+
* müsste (`<div class="tbl-wrap" tabindex="0" role="region" aria-label="…">
|
|
2544
|
+
* <table cdsTable>…`) — genau die beiden Aufgaben, die laut Ticket am ehesten
|
|
2545
|
+
* vergessen werden (fokussierbarer, benannter Scroll-Container), blieben dann
|
|
2546
|
+
* beim Konsumenten statt bei der Komponente. Der Element-Selektor hält Wrap und
|
|
2547
|
+
* Tabelle zusammen in einem Template.
|
|
2548
|
+
*
|
|
2549
|
+
* **Geprüft im laufenden Storybook (Story „Interaktiv“, Play-Funktion):**
|
|
2550
|
+
* `<table class="tbl">` liegt direkt unter `<div class="tbl-wrap">`, `<caption>`
|
|
2551
|
+
* ist ihr erstes Kind, danach folgen ohne zusätzlichen Knoten dazwischen genau
|
|
2552
|
+
* die projizierten `<thead>`/`<tbody>`-Elemente — `<ng-content>` fügt selbst
|
|
2553
|
+
* kein DOM-Element ein, es verschiebt nur die vom Konsumenten geschriebenen
|
|
2554
|
+
* nativen Elemente an ihre Stelle. Damit bleibt die Tabellenstruktur (`<table>`
|
|
2555
|
+
* → `<caption>` + `<thead>` + `<tbody>` + optional `<tfoot>`, keine fremde
|
|
2556
|
+
* Ebene dazwischen) exakt so gültig wie im rohen Markup.
|
|
2557
|
+
*
|
|
2558
|
+
* **`.tbl-wrap` ist immer fokussierbar UND immer benannt.** Das CSS gibt dem
|
|
2559
|
+
* Scroll-Container einen eigenen `:focus-visible`-Ring
|
|
2560
|
+
* (css/components.css:1463) — ein scrollbarer Bereich muss per Tastatur
|
|
2561
|
+
* erreichbar sein (WCAG 2.1.1), `tabindex="0"` steht deshalb fest im Template.
|
|
2562
|
+
* `role="region"` braucht laut HTML-AAM einen zugänglichen Namen, sonst bekäme
|
|
2563
|
+
* der Bereich keine Landmark-Rolle — weil `caption` jetzt Pflicht ist, kann
|
|
2564
|
+
* `accessibleName()` (`scrollLabel() || caption()`) nicht mehr leer werden,
|
|
2565
|
+
* `role`/`aria-label` sind deshalb unbedingt gesetzt, kein Nullfall mehr zu
|
|
2566
|
+
* behandeln. `scrollLabel` bleibt eine optionale Überschreibung (Vorrang vor
|
|
2567
|
+
* `caption`) für Fälle, in denen der sichtbare Tabellentitel für den
|
|
2568
|
+
* Scroll-Container zu lang oder zu unspezifisch wäre.
|
|
2569
|
+
*
|
|
2570
|
+
* **`.tbl-sort` bleibt außen vor.** Die Klasse existiert
|
|
2571
|
+
* (css/components.css:1477–1482), die Sortierlogik nicht — kein zugehöriges
|
|
2572
|
+
* JS in `docs/main.js` für die Beispielseiten. Ein Wrapper, der nur den
|
|
2573
|
+
* Button-Look lieferte, täuschte Funktion vor, die es nicht gibt. Siehe
|
|
2574
|
+
* `.scratch/angular-seitenbausteine/issues/19-fehlende-sortierlogik-tbl-sort.md`.
|
|
2575
|
+
*/
|
|
2576
|
+
declare class TableComponent {
|
|
2577
|
+
/** Überschrift der Tabelle (`<caption>`, erstes Kind) — Pflicht, siehe Klassendoku. */
|
|
2578
|
+
readonly caption: _angular_core.InputSignal<string>;
|
|
2579
|
+
/** Alternierende Zeilenfarbe für Tabellen mit vielen Zeilen (`.tbl--striped`). */
|
|
2580
|
+
readonly striped: _angular_core.InputSignal<boolean>;
|
|
2581
|
+
/**
|
|
2582
|
+
* Zugänglicher Name des Scroll-Containers (`aria-label` auf `.tbl-wrap`), hat
|
|
2583
|
+
* Vorrang vor `caption`. Leer = `caption` wird zum Namen (siehe Klassendoku).
|
|
2584
|
+
*/
|
|
2585
|
+
readonly scrollLabel: _angular_core.InputSignal<string>;
|
|
2586
|
+
/**
|
|
2587
|
+
* Name des Scroll-Containers: `scrollLabel` hat Vorrang, sonst `caption` —
|
|
2588
|
+
* dank Pflicht-`caption` nie leer (siehe Klassendoku).
|
|
2589
|
+
*
|
|
2590
|
+
* @internal
|
|
2591
|
+
*/
|
|
2592
|
+
protected readonly accessibleName: _angular_core.Signal<string>;
|
|
2593
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TableComponent, never>;
|
|
2594
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<TableComponent, "cds-table", never, { "caption": { "alias": "caption"; "required": true; "isSignal": true; }; "striped": { "alias": "striped"; "required": false; "isSignal": true; }; "scrollLabel": { "alias": "scrollLabel"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2597
|
+
/** Eine Vergleichsspalte (Paket/Tarif) der Vergleichstabelle. */
|
|
2598
|
+
interface CdsCompareColumn {
|
|
2599
|
+
/** Spaltenüberschrift, z. B. „Core“ oder „Pro“. */
|
|
2600
|
+
label: string;
|
|
2601
|
+
/** Hervorgehobene Spalte (`.ep-compare-pro`), z. B. das empfohlene Paket. */
|
|
2602
|
+
pro?: boolean;
|
|
2603
|
+
}
|
|
2604
|
+
/** Eine Vergleichszeile: das verglichene Merkmal plus eine Zelle je Spalte. */
|
|
2605
|
+
interface CdsCompareRow {
|
|
2606
|
+
/** Zeilenüberschrift (`<th scope="row">`), das verglichene Merkmal. */
|
|
2607
|
+
label: string;
|
|
2608
|
+
/**
|
|
2609
|
+
* Eine Zelle je Eintrag in `columns`, in derselben Reihenfolge. `boolean` → Ja/Nein-Glyphe
|
|
2610
|
+
* (`.ep-compare-yes`/`-no`) mit Screenreader-Text, `string` → die kurze Angabe als Text
|
|
2611
|
+
* (z. B. „bis 70 Mrd. Param.“).
|
|
2612
|
+
*/
|
|
2613
|
+
cells: (boolean | string)[];
|
|
2614
|
+
}
|
|
2615
|
+
/**
|
|
2616
|
+
* Compare (`cds-compare`) — Wrapper um `.ep-compare*` aus css/components.css
|
|
2617
|
+
* (css/components.css:1275–1294): die aufklappbare Vergleichstabelle für den zeilenweisen
|
|
2618
|
+
* Direktvergleich mehrerer Pakete/Tarife (Doku-Site `sec-table`, Nav-Eintrag
|
|
2619
|
+
* „Vergleichstabelle (aufklappbar)“, `docs/index.html:5182–5204`). Zehntes Ticket der
|
|
2620
|
+
* Seitenbausteine-Serie, nach `cds-table` (Ticket 09).
|
|
2621
|
+
*
|
|
2622
|
+
* **Mit Daten-Input, anders als `cds-table`.** `cds-table` projiziert `<thead>`/`<tbody>`
|
|
2623
|
+
* unverändert per `<ng-content>`, weil seine Zellen Badges, Links und `data-num` tragen können
|
|
2624
|
+
* (siehe dessen Klassendoku). Hier ist der Zellinhalt strukturell auf Ja/Nein oder eine kurze
|
|
2625
|
+
* Angabe begrenzt — ausgezählt: alle 22 Datenzellen des einzigen realen Vorkommens
|
|
2626
|
+
* (`docs/index.html:11970–12038`, 11 Zeilen × 2 Spalten, ohne die Zeilenüberschriften) sind
|
|
2627
|
+
* entweder ein `.ep-compare-yes`/`-no`-Marker (18×, davon 14× „Ja“, 4× „Nein“) oder ein kurzer
|
|
2628
|
+
* Text wie „bis 70 Mrd. Param.“ bzw. „30 Min.“ (4×) — nie ein Badge, Link oder anderes Markup.
|
|
2629
|
+
* Weil Spaltenzahl, `pro`-Spalte und Zellinhalt damit vollständig aus zwei Arrays (`columns`,
|
|
2630
|
+
* `rows`) hervorgehen, würde `<ng-content>` beim Konsumenten nur denselben `@for`-Code
|
|
2631
|
+
* duplizieren, ohne eine Freiheit zu gewinnen, die die Zell-Typisierung (`boolean | string`)
|
|
2632
|
+
* ohnehin ausschließt.
|
|
2633
|
+
*
|
|
2634
|
+
* **Ja/Nein-Zellen tragen einen Screenreader-Text, übernommen statt erfunden.** Im einzigen
|
|
2635
|
+
* realen Vorkommen steht hinter jeder Glyphe ein `.sr-only`-Text, ausnahmslos:
|
|
2636
|
+
* `<span class="ep-compare-yes" aria-hidden="true">✓</span><span class="sr-only">Enthalten</span>`
|
|
2637
|
+
* (14×) bzw. `<span class="ep-compare-no" aria-hidden="true">−</span><span class="sr-only">Nicht
|
|
2638
|
+
* enthalten</span>` (4×, „−“ ist U+2212 Minus, kein Bindestrich). Genau diese Zeichen und dieser
|
|
2639
|
+
* Wortlaut, keine eigene Formulierung — Farbe und Glyphe allein tragen keine Bedeutung
|
|
2640
|
+
* (WCAG 1.4.1), der `.sr-only`-Text ist deshalb kein optionaler Zusatz, sondern fest verdrahtet,
|
|
2641
|
+
* nicht per Input überschreibbar.
|
|
2642
|
+
*
|
|
2643
|
+
* **`caption` ist Pflicht (`input.required<string>()`), abweichend von der Ticket-Skizze (dort
|
|
2644
|
+
* kein Input dafür vorgesehen) — dieselbe Begründung wie bei `cds-table`.** Die eigene Doku
|
|
2645
|
+
* (`storybook-angular/src/docs/komponenten/tabelle.mdx:91`) nennt `<caption class="sr-only">`
|
|
2646
|
+
* ausdrücklich als Teil des Bauteils, und das einzige reale Vorkommen hat eine. Anders als bei
|
|
2647
|
+
* `cds-table` ist sie hier `.sr-only`, nicht sichtbar: der Klartext steht schon im `<summary>`
|
|
2648
|
+
* (`summary`-Input), die `<caption>` wiederholt ihn nur für Screenreader, die nach dem Aufklappen
|
|
2649
|
+
* direkt in die Tabelle springen, ohne den Summary-Text erneut vorgelesen zu bekommen.
|
|
2650
|
+
*
|
|
2651
|
+
* **`rowsLabel` (erste Kopfzelle, `<th scope="col">Funktion</th>` im Mockup) ist dagegen Beiwerk,
|
|
2652
|
+
* optional mit Default `''`.** Auch dafür sieht die Ticket-Skizze keinen Input vor. Den Text fest
|
|
2653
|
+
* im Template zu verdrahten hieße, AI.Box-Copy in einen wiederverwendbaren Wrapper zu backen
|
|
2654
|
+
* (ADR-0007 §2); ihn ganz wegzulassen wich ohne CSS-Befund vom einzigen Beleg ab, ohne dass die
|
|
2655
|
+
* Doku das verlangt. Anders als `caption` ist die Kopfzelle nicht barrierefreiheitsrelevant
|
|
2656
|
+
* Pflicht: ein leeres `<th scope="col">` bleibt eine gültige, in Vergleichsmatrizen verbreitete
|
|
2657
|
+
* Ecke ohne eigenen Namen. Deshalb Beiwerk, kein Inhalt — das Beispielwort „Funktion“ steht in
|
|
2658
|
+
* der Story, nicht in der Klasse.
|
|
2659
|
+
*
|
|
2660
|
+
* **Natives `<details>`/`<summary>` bleibt erhalten, kein nachgebautes Disclosure** (Ticket-
|
|
2661
|
+
* Vorgabe, analog `cds-faq`): Tastaturbedienung und Toggle-Verhalten kommen vom Browser, das CSS
|
|
2662
|
+
* hängt direkt an `[open]` (`.ep-compare[open] .ep-compare-caret`). `<details class="ep-compare">`
|
|
2663
|
+
* sitzt deshalb im TEMPLATE, nicht am Host — derselbe Grund wie bei `cds-table`s
|
|
2664
|
+
* `.tbl-wrap`/`.tbl`: ein `<cds-compare>`-Host ist ein unbekanntes Custom Element, kein echtes
|
|
2665
|
+
* `<details>`, und kann dessen native Disclosure-Semantik nicht annehmen.
|
|
2666
|
+
*
|
|
2667
|
+
* **Element-Selektor (ADR-0008-Standardfall).** `.ep-compare` sitzt im einzigen realen Vorkommen
|
|
2668
|
+
* als gewöhnlicher Block-Nachfahre in `.ep-section` (`docs/index.html:11969`) — kein Grid-/
|
|
2669
|
+
* Flex-Kind, keine `col-*`-Klasse vom Konsumenten, kein Tag-Wechsel (immer `<details>`). Keines
|
|
2670
|
+
* der drei ADR-0008-Kriterien greift, ein Attributselektor brächte hier nichts.
|
|
2671
|
+
*
|
|
2672
|
+
* **`open` ist ein reiner Anfangszustand, keine Zwei-Wege-Bindung.** Gebunden über
|
|
2673
|
+
* `[attr.open]="open() ? '' : null"`: Angular schreibt das Attribut nur, wenn sich der GEPRÜFTE
|
|
2674
|
+
* Ausdruckswert seit dem letzten Change-Detection-Lauf ändert. `open()` bleibt nach dem ersten
|
|
2675
|
+
* Rendern konstant (die Komponente schreibt nie in dieses Signal zurück), ein nachfolgender
|
|
2676
|
+
* nativer Toggle wird deshalb nicht rückgängig gemacht — geprüft in der Story „Interaktiv“
|
|
2677
|
+
* (Play-Funktion: Klick öffnet, `toggled` feuert `true`, ein zweiter, vom Toggle unabhängiger
|
|
2678
|
+
* Interaktionsschritt lässt die Tabelle weiterhin offen).
|
|
2679
|
+
*/
|
|
2680
|
+
declare class CompareComponent {
|
|
2681
|
+
/** Text im `<summary>`, z. B. „Alle Funktionen vergleichen“. */
|
|
2682
|
+
readonly summary: _angular_core.InputSignal<string>;
|
|
2683
|
+
/** `<caption class="sr-only">` der Tabelle — Pflicht, siehe Klassendoku. */
|
|
2684
|
+
readonly caption: _angular_core.InputSignal<string>;
|
|
2685
|
+
/** Überschrift der ersten Spalte (Zeilentitel, z. B. „Funktion“) — Beiwerk, siehe Klassendoku. */
|
|
2686
|
+
readonly rowsLabel: _angular_core.InputSignal<string>;
|
|
2687
|
+
/** Anfangszustand des `<details>` — reine Vorbelegung, siehe Klassendoku. */
|
|
2688
|
+
readonly open: _angular_core.InputSignal<boolean>;
|
|
2689
|
+
/** Vergleichsspalten (Pakete/Tarife), in Render-Reihenfolge. */
|
|
2690
|
+
readonly columns: _angular_core.InputSignal<CdsCompareColumn[]>;
|
|
2691
|
+
/** Vergleichszeilen; `cells` hat je Zeile genau einen Eintrag pro Spalte in `columns`. */
|
|
2692
|
+
readonly rows: _angular_core.InputSignal<CdsCompareRow[]>;
|
|
2693
|
+
/** Feuert bei jedem Auf- oder Zuklappen mit dem neuen Zustand. */
|
|
2694
|
+
readonly toggled: _angular_core.OutputEmitterRef<boolean>;
|
|
2695
|
+
/** @internal */
|
|
2696
|
+
protected onToggle(event: Event): void;
|
|
2697
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<CompareComponent, never>;
|
|
2698
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<CompareComponent, "cds-compare", never, { "summary": { "alias": "summary"; "required": true; "isSignal": true; }; "caption": { "alias": "caption"; "required": true; "isSignal": true; }; "rowsLabel": { "alias": "rowsLabel"; "required": false; "isSignal": true; }; "open": { "alias": "open"; "required": false; "isSignal": true; }; "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "rows": { "alias": "rows"; "required": true; "isSignal": true; }; }, { "toggled": "toggled"; }, never, never, true, never>;
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2701
|
+
/** Größenvarianten von `cds-avatar` (`.article-avatar` / `-lg` / `-xl`). */
|
|
2702
|
+
type CdsAvatarSize = 'sm' | 'lg' | 'xl';
|
|
2703
|
+
/**
|
|
2704
|
+
* Avatar (`div[cdsAvatar]`) — Wrapper um `.article-avatar` aus css/components.css
|
|
2705
|
+
* (css/components.css:1520–1536): der Initialen-/Foto-Kreis im Meta-Strip eines
|
|
2706
|
+
* Wissensbeitrags (`.article-meta-author`), in der Author-Card (`-lg`) und im
|
|
2707
|
+
* Team-Tile-Grid (`-xl`). Elftes Ticket der Seitenbausteine-Serie.
|
|
2708
|
+
*
|
|
2709
|
+
* **Attributselektor (ADR-0008), gemessen an der Geschwister-Kette.** Ausgezählt in
|
|
2710
|
+
* `docs/index.html`: 8 der 11 Instanzen der Grundgröße sitzen als direkte, adjazente
|
|
2711
|
+
* Geschwister in einer von 3 `.article-avatar-stack`-Kacheln (2 Avatare in Zeilen
|
|
2712
|
+
* 8343–8344, je 3 in Zeilen 8373–8375 und 8404–8406) — genau dort greift
|
|
2713
|
+
* `.article-avatar-stack .article-avatar + .article-avatar`
|
|
2714
|
+
* (css/components.css:1540–1541), eine Geschwister-Kette (Kriterium `spec.md` 2b).
|
|
2715
|
+
* Ein Element-Selektor `cds-avatar`, der `.article-avatar` auf einem INNEREN Element
|
|
2716
|
+
* rendert (wie `cds-facts` es mit `<dl>` tut, weil `<dl>` ein Custom Element nicht
|
|
2717
|
+
* annehmen kann), würde diese Kette brechen: die beiden `.article-avatar`-Knoten
|
|
2718
|
+
* lägen dann je eine Ebene UNTER ihrem eigenen `<cds-avatar>`-Host und wären keine
|
|
2719
|
+
* Geschwister mehr — `+` verlangt denselben Elternknoten, egal wie dicht die Hosts
|
|
2720
|
+
* selbst nebeneinanderstehen. Eine zweite, ebenfalls tragfähige Variante
|
|
2721
|
+
* (Element-Selektor MIT `host: { class: 'article-avatar' }`, ohne innere
|
|
2722
|
+
* Wrapper-Ebene) hätte dieselbe Geschwister-Kette ebenso erfüllt — verworfen zugunsten
|
|
2723
|
+
* des Attributselektors nur wegen des zweiten, unabhängigen Grundes unten, nicht weil
|
|
2724
|
+
* sie die Kette nicht gelöst hätte.
|
|
2725
|
+
*
|
|
2726
|
+
* **Gemessen, nicht nur hergeleitet.** Story „Avatar-Stapel“ (Play-Funktion) rendert
|
|
2727
|
+
* drei `div[cdsAvatar]` in einem `cds-avatar-stack` und misst
|
|
2728
|
+
* `getBoundingClientRect()` der drei `.article-avatar`-Knoten: bei 40 px Kachelbreite
|
|
2729
|
+
* und `margin-left:-12px` (css/components.css:1541) überlappen der zweite und dritte
|
|
2730
|
+
* Kreis den jeweils vorherigen um 12 px (Differenz der `left`-Koordinaten = 28 px,
|
|
2731
|
+
* nicht 40 px) — plus Screenshot-Baseline (`visual-snapshots/…avatar-stapel.png`) als
|
|
2732
|
+
* visuelle Bestätigung, nicht nur die Zahl.
|
|
2733
|
+
*
|
|
2734
|
+
* **Zweiter, unabhängiger Grund: Wiederverwendung als Projektions-Selektor.**
|
|
2735
|
+
* `cds-article-header` projiziert den Avatar über
|
|
2736
|
+
* `<ng-content select="[cdsAvatar]">` (siehe dessen Klassendoku) — dasselbe Attribut,
|
|
2737
|
+
* das hier den Host selektiert, dient dem Elternteil zugleich als
|
|
2738
|
+
* Content-Projection-Selektor. Ein Element-Selektor bräuchte dafür eine zusätzliche,
|
|
2739
|
+
* eigens erfundene Marker-Klasse; der Attributselektor liefert die Zielscheibe für
|
|
2740
|
+
* beide Zwecke aus einer Hand, ohne dass der Konsument zwei Attribute schreiben muss.
|
|
2741
|
+
*
|
|
2742
|
+
* **Initialen werden aus `name` abgeleitet, kein eigener Input** (Ticket-Vorgabe):
|
|
2743
|
+
* erstes Zeichen des ersten und des letzten durch Whitespace getrennten Worts, groß
|
|
2744
|
+
* — ein einzelnes Wort liefert dessen erste zwei Zeichen. Zwei Inputs für dieselbe
|
|
2745
|
+
* Information (Name UND Initialen) könnten auseinanderlaufen, sobald nur eines
|
|
2746
|
+
* gepflegt wird.
|
|
2747
|
+
*
|
|
2748
|
+
* **`aria-hidden="true"` fest verdrahtet, kein Input.** Ausgezählt: von 53
|
|
2749
|
+
* `.article-avatar`-Instanzen (11 Grundgröße + 17 `-lg` + 25 `-xl`) tragen 45 das
|
|
2750
|
+
* Attribut direkt am Element; die restlichen 8 sind genau die Grundgrößen-Avatare
|
|
2751
|
+
* INNERHALB von `.article-avatar-stack`, wo bereits der Stack-Container selbst
|
|
2752
|
+
* `aria-hidden="true"` trägt (siehe `AvatarStackComponent`) — dort wäre ein zweites
|
|
2753
|
+
* `aria-hidden` am Kind redundant, aber unschädlich: verschachteltes `aria-hidden`
|
|
2754
|
+
* ändert nichts am Ergebnis, der Teilbaum ist so oder so für Assistenztechnologie
|
|
2755
|
+
* unsichtbar. Der Avatar trägt es deshalb immer: 100 % Deckung mit dem Mockup im
|
|
2756
|
+
* Alleinstand, harmlose Redundanz im Stapel.
|
|
2757
|
+
*/
|
|
2758
|
+
declare class AvatarComponent {
|
|
2759
|
+
/** Voller Name — Quelle der Initialen und des Bild-`alt`-Texts. */
|
|
2760
|
+
readonly name: _angular_core.InputSignal<string>;
|
|
2761
|
+
/** Größe (`.article-avatar` = sm, `-lg`, `-xl`). */
|
|
2762
|
+
readonly size: _angular_core.InputSignal<CdsAvatarSize>;
|
|
2763
|
+
/** Markenbereich → `data-area` (Füllfarbe der Initialen-Kachel). */
|
|
2764
|
+
readonly area: _angular_core.InputSignal<CdsArea | undefined>;
|
|
2765
|
+
/** Bildquelle; gesetzt → `<img>` ersetzt die Initialen. */
|
|
2766
|
+
readonly src: _angular_core.InputSignal<string>;
|
|
2767
|
+
/**
|
|
2768
|
+
* Erstes Zeichen des ersten und letzten Worts aus `name`, groß — bei einem
|
|
2769
|
+
* einzelnen Wort dessen erste zwei Zeichen.
|
|
2770
|
+
*
|
|
2771
|
+
* @internal
|
|
2772
|
+
*/
|
|
2773
|
+
protected readonly initials: _angular_core.Signal<string>;
|
|
2774
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<AvatarComponent, never>;
|
|
2775
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<AvatarComponent, "div[cdsAvatar]", never, { "name": { "alias": "name"; "required": true; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; "src": { "alias": "src"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2778
|
+
/**
|
|
2779
|
+
* AvatarStack (`cds-avatar-stack`) — Wrapper um `.article-avatar-stack` aus
|
|
2780
|
+
* css/components.css (css/components.css:1538–1545): mehrere `div[cdsAvatar]`
|
|
2781
|
+
* überlappend für Mehrfach-Autor:innen im Meta-Strip, mit optionalem
|
|
2782
|
+
* „+N“-Indikator (`.article-avatar-more`) ab vier Personen (siehe
|
|
2783
|
+
* `wissensbeitrag.mdx`, Abschnitt „Mehrere Autor:innen“).
|
|
2784
|
+
*
|
|
2785
|
+
* **Element-Selektor, ADR-0008-Standardfall.** `.article-avatar-stack` ist selbst
|
|
2786
|
+
* nie Grid-/Flex-Kind mit Stretch-Bedarf — sein einziger reale Elternkontext,
|
|
2787
|
+
* `.article-meta-author`, ist `display:flex;align-items:center` (center, nicht
|
|
2788
|
+
* stretch, css/components.css:1514), variiert nicht das Tag und ist selbst nie Teil
|
|
2789
|
+
* einer `+`-Kette (nur SEINE Kinder sind das, siehe `AvatarComponent`). Die Klasse
|
|
2790
|
+
* darf deshalb direkt am `<cds-avatar-stack>`-Host sitzen.
|
|
2791
|
+
*
|
|
2792
|
+
* **`<ng-content>` fügt kein eigenes Element ein**, die projizierten
|
|
2793
|
+
* `div[cdsAvatar]` bleiben deshalb direkte, adjazente Geschwister des optionalen
|
|
2794
|
+
* `.article-avatar-more`-Kreises — Voraussetzung für
|
|
2795
|
+
* `.article-avatar-stack .article-avatar + .article-avatar-more`
|
|
2796
|
+
* (css/components.css:1541).
|
|
2797
|
+
*
|
|
2798
|
+
* **`aria-hidden="true"` fest verdrahtet.** Ausgezählt: alle 3 realen
|
|
2799
|
+
* `.article-avatar-stack`-Vorkommen (`docs/index.html:8342,8372,8403`) tragen es —
|
|
2800
|
+
* dieselbe Begründung wie in `wissensbeitrag.mdx` („Barrierefreiheit“): Namen stehen
|
|
2801
|
+
* vollständig als Text in `.article-meta-name`, der Stapel selbst trägt keine eigene
|
|
2802
|
+
* Information, der „+N“-Kreis ebenfalls nicht (die Restzahl steht redundant im
|
|
2803
|
+
* Namenstext, siehe Story).
|
|
2804
|
+
*/
|
|
2805
|
+
declare class AvatarStackComponent {
|
|
2806
|
+
/** Restzahl für den „+N“-Kreis (`.article-avatar-more`); 0 = kein Indikator. */
|
|
2807
|
+
readonly more: _angular_core.InputSignal<number>;
|
|
2808
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<AvatarStackComponent, never>;
|
|
2809
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<AvatarStackComponent, "cds-avatar-stack", never, { "more": { "alias": "more"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
/** Ein Eintrag der Breadcrumb-Leiste (`.article-breadcrumb`). */
|
|
2813
|
+
interface CdsArticleBreadcrumbItem {
|
|
2814
|
+
/** Sichtbarer Text. */
|
|
2815
|
+
label: string;
|
|
2816
|
+
/** Linkziel. Am letzten Eintrag wirkungslos, siehe Klassendoku. */
|
|
2817
|
+
href?: string;
|
|
2818
|
+
}
|
|
2819
|
+
/**
|
|
2820
|
+
* ArticleHeader (`cds-article-header`) — Wrapper um `.article-header` aus
|
|
2821
|
+
* css/components.css (css/components.css:1486–1490, 1508–1519): der zentrierte Kopf
|
|
2822
|
+
* eines Wissensbeitrags (Breadcrumb, optionale Pill, H1, Lead, Meta-Strip). Elftes
|
|
2823
|
+
* Ticket der Seitenbausteine-Serie. Hauptvorlage ist
|
|
2824
|
+
* `storybook-angular/src/docs/seitenmuster/wissensbeitrag.mdx`, Abschnitt „Article
|
|
2825
|
+
* Header“ — dort steht das für dieses Ticket verbindliche Markup samt Begründung,
|
|
2826
|
+
* nicht nur das Mockup in `docs/index.html`.
|
|
2827
|
+
*
|
|
2828
|
+
* **Breadcrumb sitzt im Header — abweichend von der allgemeinen Navigationsregel,
|
|
2829
|
+
* bewusst der Ticket-Vorlage folgend.** `docs/index.html:2423` dokumentiert für
|
|
2830
|
+
* Unterseiten allgemein, wörtlich: „Den Breadcrumb nicht in einen zentrierten
|
|
2831
|
+
* `.article-header` einbetten, sonst wird er mittig ausgerichtet und bekommt einen
|
|
2832
|
+
* abweichenden Abstand zur Nav“ (Tabellenzeile „Nicht“ der Breadcrumb-Doku) — und
|
|
2833
|
+
* ausgezählt
|
|
2834
|
+
* sind es genau die beiden EINZIGEN vollständigen Beispielseiten mit
|
|
2835
|
+
* `.article-header` (Stellenanzeige `docs/index.html:11035`, Wissensbeitrag · KI
|
|
2836
|
+
* `docs/index.html:15020`), die dieser Regel folgen: beide setzen den Breadcrumb in
|
|
2837
|
+
* eine EIGENE, linksbündige `.ep-section` VOR dem Header (`docs/index.html:
|
|
2838
|
+
* 11023–11031` bzw. `15010–15015`). Dem stehen 6 ISOLIERTE Doku-Demos gegenüber, die
|
|
2839
|
+
* den Breadcrumb INNERHALB von `.article-header` zeigen: 5 kleine Referenz-Boxen in
|
|
2840
|
+
* der Doku-Sektion selbst (`docs/index.html:7850`, `8304`, `8331`, `8361`, `8392` —
|
|
2841
|
+
* ohne Topnav/Hero/Footer, reine Bauteil-Illustrationen) plus das Code-Beispiel in
|
|
2842
|
+
* `wissensbeitrag.mdx`. Das Ticket benennt genau diese Doku (nicht die
|
|
2843
|
+
* Beispielseiten) als Hauptvorlage. Diese Komponente folgt der Ticket-Vorgabe
|
|
2844
|
+
* (Breadcrumb als Input, im Header) und dokumentiert den Widerspruch als eigenen
|
|
2845
|
+
* Befund
|
|
2846
|
+
* (`.scratch/angular-seitenbausteine/issues/20-breadcrumb-in-oder-vor-article-header.md`).
|
|
2847
|
+
* Konsumenten, die der allgemeinen Regel folgen wollen (wie die beiden echten
|
|
2848
|
+
* Beispielseiten), lassen `breadcrumb` leer und setzen die Leiste selbst davor
|
|
2849
|
+
* (siehe Story „Ohne Breadcrumb“).
|
|
2850
|
+
*
|
|
2851
|
+
* **Letzter Breadcrumb-Eintrag immer ohne Link, mit `aria-current="page"`** — so vom
|
|
2852
|
+
* Ticket als Akzeptanzkriterium gefordert. Die beiden Wissensbeitrag-Inline-Beispiele
|
|
2853
|
+
* (`docs/index.html:7850–7856`, `wissensbeitrag.mdx`) zeigen dafür nur 2 Einträge,
|
|
2854
|
+
* BEIDE als `<a>`, ohne `aria-current` — eine zur Kürze vereinfachte Doku-Skizze,
|
|
2855
|
+
* kein vollständiges Barrierefreiheits-Beispiel. Das volle, korrekte Muster (Trenner
|
|
2856
|
+
* dekorativ, letzter Eintrag `<span aria-current="page">` ohne Link) steht an
|
|
2857
|
+
* anderer Stelle exakt so vor (`docs/index.html:2406–2412` allgemeine
|
|
2858
|
+
* Breadcrumb-Doku, `docs/index.html:11023–11030` Stellenanzeige) und ist zusätzlich
|
|
2859
|
+
* eine explizite Akzeptanzbedingung des Tickets — die Komponente folgt deshalb
|
|
2860
|
+
* diesem Muster für den letzten Array-Eintrag, unabhängig von einem dort eventuell
|
|
2861
|
+
* gesetzten `href`.
|
|
2862
|
+
*
|
|
2863
|
+
* **Keine bereichsgefärbte Breadcrumb-Verlinkung (`.t-{area}`).** Die
|
|
2864
|
+
* Inline-Beispiele färben ihren zweiten (dort letzten) Eintrag mit `.t-ki`/`.t-es`/
|
|
2865
|
+
* `.t-wo`; das Ticket sieht dafür aber kein API-Feld vor (`{label, href?}`, kein
|
|
2866
|
+
* `area` pro Eintrag), und eine Zuordnung „Bereichsfarbe auf den vorletzten
|
|
2867
|
+
* Eintrag“ wäre geraten, nicht aus der Doku ableitbar — zumal der gefärbte Eintrag
|
|
2868
|
+
* dort ein Link ist, kein `aria-current`-Element, also nicht deckungsgleich mit dem
|
|
2869
|
+
* oben umgesetzten Muster. Bleibt bewusst außen vor.
|
|
2870
|
+
*
|
|
2871
|
+
* **Pill über `cds-pill`, kein direkt komponiertes `<span class="pill">`.** Geprüft
|
|
2872
|
+
* per `npx storybook tools docs show --id komponenten-chips-badges-pills-pill`:
|
|
2873
|
+
* `PillComponent`s Host ist `<cds-pill>`, `.pill` sitzt eine Ebene darunter auf
|
|
2874
|
+
* einem inneren `<span>` (anders als bei `FeaturedCardComponent`, wo genau das wegen
|
|
2875
|
+
* `.card-featured-body>.pill`-Kindselektoren nicht ginge). `.article-header`/`.pill`
|
|
2876
|
+
* haben keinen solchen Kindselektor (css/components.css:1486–1507 geprüft) — die
|
|
2877
|
+
* zusätzliche Ebene ist hier folgenlos, deshalb bleibt `cds-pill` als echte
|
|
2878
|
+
* Komponente eingesetzt, keine Klassen-Duplikation nötig.
|
|
2879
|
+
*
|
|
2880
|
+
* **Pille rendert nur, wenn `area` UND `pill` gesetzt sind — kein erfundener
|
|
2881
|
+
* Default.** `PillComponent.area` ist als `CdsArea` mit einem eigenen Default
|
|
2882
|
+
* (`'ki'`) typisiert; diese Komponente führt `area` dagegen als `CdsArea | undefined`
|
|
2883
|
+
* (keine Konfigurations-Vorgabe laut Ticket-API). Ein erster Entwurf reichte
|
|
2884
|
+
* `area()` per `as CdsArea`-Cast durch, damit ein `undefined`-Binding
|
|
2885
|
+
* `cds-pill`s eigenen Default überschreiben kann — eine Typ-Lüge, um einen Fall
|
|
2886
|
+
* abzudecken, der beim Nachzählen gar nicht vorkommt: **alle 9** `.pill`-Vorkommen
|
|
2887
|
+
* im Article-Header-Kontext (`docs/index.html:7856, 7912–7914, 8310, 8337, 8367,
|
|
2888
|
+
* 8398, 15021`) tragen ein `data-area`, keines ohne Bereich. Die Pille rendert
|
|
2889
|
+
* deshalb nur innerhalb von `@if (area(); as pillArea)` (narrowt `CdsArea |
|
|
2890
|
+
* undefined` sauber auf `CdsArea`, ohne Cast) — ist `pill()` gesetzt, `area()`
|
|
2891
|
+
* aber nicht, bleibt die Pille schlicht weg, statt eine geratene Bereichsfarbe zu
|
|
2892
|
+
* zeigen. Gepinnt in der Story „Pille ohne Bereich“.
|
|
2893
|
+
*
|
|
2894
|
+
* **`pillAriaLabel`, wie bei `FeaturedCardComponent`.** `PillComponent`s eigener
|
|
2895
|
+
* Default („Bereich `<label>`“) passt nur, wenn die Pille einen Bereichsnamen trägt
|
|
2896
|
+
* (Stellenanzeige, `docs/index.html:11036`: „Bereich Effektive Software“). Im
|
|
2897
|
+
* Wissensbeitrag trägt die Pille dagegen die Lesezeit (`wissensbeitrag.mdx`,
|
|
2898
|
+
* Abschnitt „Pill · Lesezeit“: `aria-label="Lesezeit 8 Minuten"`) — ein Text, den
|
|
2899
|
+
* die Komponente nicht zuverlässig aus einem freien `pill`-String wie
|
|
2900
|
+
* „8 min Lesezeit“ herleiten kann (keine Zahl zu parsen, keine Garantie, dass der
|
|
2901
|
+
* Text überhaupt eine Lesezeit ist). `pillAriaLabel` bleibt deshalb optional, ohne
|
|
2902
|
+
* eigenen Default: ungesetzt geht `undefined` direkt an `cds-pill`s `ariaLabel`
|
|
2903
|
+
* durch, das dann auf seinen eigenen Default („Bereich `<pill>`“) zurückfällt —
|
|
2904
|
+
* keine Logik-Duplikation nötig, weil hier die echte `PillComponent` verwendet
|
|
2905
|
+
* wird, keine Klassen-Nachbildung.
|
|
2906
|
+
*
|
|
2907
|
+
* **`date`/`dateLabel` getrennt, ANDERS als `cds-stoerer`.** `cds-stoerer`
|
|
2908
|
+
* formatiert ein reines ISO-Datum selbst nach `de-DE`
|
|
2909
|
+
* (`stoerer.component.ts:formattedDate`, inkl. manuellem Aufbau der
|
|
2910
|
+
* `Date`-Komponenten gegen den UTC-Mitternacht-Fallstrick von `new Date(iso)`).
|
|
2911
|
+
* Diese Komponente tut das NICHT: `date` liefert nur den maschinenlesbaren
|
|
2912
|
+
* `datetime`-Wert, `dateLabel` die sichtbare Schreibweise — vom Ticket ausdrücklich
|
|
2913
|
+
* als zwei getrennte Inputs vorgegeben. Begründung der Abweichung: Erstens deckt
|
|
2914
|
+
* sich das mit jeder realen Instanz (6 von 6 `.article-meta-date`-Vorkommen tragen
|
|
2915
|
+
* einen redaktionell gesetzten Text wie „13. Mai 2026“, keinen von
|
|
2916
|
+
* `Intl.DateTimeFormat` erzeugten) — anders als der Störer, dessen Kacheln NUR ein
|
|
2917
|
+
* ISO-Datum ohne separaten Text führen. Zweitens umgeht die Trennung den
|
|
2918
|
+
* Zeitzonen-Fallstrick vollständig, statt ihn zu beheben: ohne eigene Formatierung
|
|
2919
|
+
* konstruiert diese Komponente nie ein `Date`-Objekt aus `date()`, die
|
|
2920
|
+
* UTC-Mitternacht-Falle kann hier gar nicht erst auftreten. Ist `dateLabel` leer,
|
|
2921
|
+
* zeigt die Komponente `date()` unverändert als sichtbaren Text (bewusst als
|
|
2922
|
+
* ISO-Form, nicht geraten formatiert) — in der Story „Interaktiv“ als Warnfall extra
|
|
2923
|
+
* benannt, damit niemand das versehentlich auf eine Produktionsseite bringt.
|
|
2924
|
+
*
|
|
2925
|
+
* **Meta-Strip nur, wenn es etwas zu zeigen gibt.** `authorName`/`date` sind laut
|
|
2926
|
+
* Ticket-API Beiwerk (Default `''`); ausgezählt haben alle 6 realen
|
|
2927
|
+
* `.article-meta`-Vorkommen sowohl Autor als auch Datum, eine Stellenanzeige
|
|
2928
|
+
* (`docs/index.html:11035`) nutzt `.article-header` dagegen KOMPLETT OHNE
|
|
2929
|
+
* `.article-meta` (dort ein `<dl>` aus Job-Fakten statt Autor/Datum) — dieser
|
|
2930
|
+
* abweichenden Verwendung folgt diese Komponente nicht (sie hat in der Ticket-API
|
|
2931
|
+
* keinen Platz), sie belegt aber, dass ein leerer Meta-Strip ein realer Fall ist.
|
|
2932
|
+
* `.article-meta` (und darin `.article-meta-author`/`.article-meta-sep`) rendern
|
|
2933
|
+
* deshalb nur, wenn `authorName()` bzw. `date()` tatsächlich etwas liefern; der
|
|
2934
|
+
* Trenner nur, wenn beide Seiten vorhanden sind.
|
|
2935
|
+
*
|
|
2936
|
+
* **Avatar projiziert über `[cdsAvatar]`, PLUS `cds-avatar-stack`.** Die
|
|
2937
|
+
* Ticket-Skizze nennt nur `<ng-content select="[cdsAvatar]">`; ergänzt um den
|
|
2938
|
+
* Tag-Selektor `cds-avatar-stack`, weil `AvatarStackComponent` (siehe dort) ein
|
|
2939
|
+
* Element-Selektor ist, kein `[cdsAvatar]`-Attribut trägt und sonst nicht in den
|
|
2940
|
+
* Meta-Strip projiziert werden könnte — nötig für die vom Ticket geforderte Story
|
|
2941
|
+
* „Mehrere Autor:innen“.
|
|
2942
|
+
*
|
|
2943
|
+
* **Host als Block.** `.article-header` sitzt am Host und setzt nur
|
|
2944
|
+
* `max-width:880px;margin:0 auto`. An einem unbekannten, also `display:inline`
|
|
2945
|
+
* gerenderten Element greift beides nicht, die Block-Kinder liefen über die
|
|
2946
|
+
* 880 px hinaus. `:host{display:block}` wie bei `cds-hero-image` und
|
|
2947
|
+
* `cds-footer-main`; Story „Breite begrenzt“ pinnt das.
|
|
2948
|
+
*/
|
|
2949
|
+
declare class ArticleHeaderComponent {
|
|
2950
|
+
/** Titel (`.article-title`, `<h1>`). */
|
|
2951
|
+
readonly title: _angular_core.InputSignal<string>;
|
|
2952
|
+
/** Anreißer unter dem Titel (`.article-lead`), leer = kein Lead. */
|
|
2953
|
+
readonly lead: _angular_core.InputSignal<string>;
|
|
2954
|
+
/** Breadcrumb-Pfad; letzter Eintrag rendert ohne Link mit `aria-current="page"`. */
|
|
2955
|
+
readonly breadcrumb: _angular_core.InputSignal<CdsArticleBreadcrumbItem[]>;
|
|
2956
|
+
/** Pille über dem Titel (`.pill`, via `cds-pill`), leer = keine Pille. */
|
|
2957
|
+
readonly pill: _angular_core.InputSignal<string>;
|
|
2958
|
+
/** `aria-label` der Pille überschreiben, siehe Klassendoku. */
|
|
2959
|
+
readonly pillAriaLabel: _angular_core.InputSignal<string | undefined>;
|
|
2960
|
+
/** Markenbereich → `data-area` der Pille; ungesetzt = keine Pille (siehe Klassendoku). */
|
|
2961
|
+
readonly area: _angular_core.InputSignal<CdsArea | undefined>;
|
|
2962
|
+
/** Name im Meta-Strip (`.article-meta-name`), leer = kein Meta-Strip-Autorblock. */
|
|
2963
|
+
readonly authorName: _angular_core.InputSignal<string>;
|
|
2964
|
+
/** Rolle im Meta-Strip (`.article-meta-role`), leer = keine Rollenzeile. */
|
|
2965
|
+
readonly authorRole: _angular_core.InputSignal<string>;
|
|
2966
|
+
/** ISO-Datum (`JJJJ-MM-TT`) → `<time datetime>`, leer = kein Datum. */
|
|
2967
|
+
readonly date: _angular_core.InputSignal<string>;
|
|
2968
|
+
/** Sichtbare Schreibweise des Datums; leer = `date` steht unverändert sichtbar. */
|
|
2969
|
+
readonly dateLabel: _angular_core.InputSignal<string>;
|
|
2970
|
+
/** @internal */
|
|
2971
|
+
protected readonly hasAuthor: _angular_core.Signal<boolean>;
|
|
2972
|
+
/** @internal */
|
|
2973
|
+
protected readonly hasDate: _angular_core.Signal<boolean>;
|
|
2974
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ArticleHeaderComponent, never>;
|
|
2975
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ArticleHeaderComponent, "cds-article-header", never, { "title": { "alias": "title"; "required": true; "isSignal": true; }; "lead": { "alias": "lead"; "required": false; "isSignal": true; }; "breadcrumb": { "alias": "breadcrumb"; "required": false; "isSignal": true; }; "pill": { "alias": "pill"; "required": false; "isSignal": true; }; "pillAriaLabel": { "alias": "pillAriaLabel"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; "authorName": { "alias": "authorName"; "required": false; "isSignal": true; }; "authorRole": { "alias": "authorRole"; "required": false; "isSignal": true; }; "date": { "alias": "date"; "required": false; "isSignal": true; }; "dateLabel": { "alias": "dateLabel"; "required": false; "isSignal": true; }; }, {}, never, ["[cdsAvatar], cds-avatar-stack"], true, never>;
|
|
2976
|
+
}
|
|
2977
|
+
|
|
2978
|
+
/** Ein Eintrag des Inhaltsverzeichnisses (`.article-toc-list li`). */
|
|
2979
|
+
interface CdsArticleTocItem {
|
|
2980
|
+
/** Sichtbarer Linktext, i. d. R. die Überschrift des Abschnitts. */
|
|
2981
|
+
label: string;
|
|
2982
|
+
/** Sprungziel, i. d. R. der Anker der zugehörigen `<h2>` im Article Body. */
|
|
2983
|
+
href: string;
|
|
2984
|
+
}
|
|
2985
|
+
/**
|
|
2986
|
+
* ArticleToc (`cds-article-toc`) — Wrapper um `.article-toc*` aus css/components.css
|
|
2987
|
+
* (css/components.css:1577–1589): das aufklappbare Inhaltsverzeichnis am Anfang eines
|
|
2988
|
+
* Wissensbeitrags. Zwölftes Ticket der Seitenbausteine-Serie („Artikel-Körper“).
|
|
2989
|
+
* Ausgezählt: genau 2 reale Vorkommen, beide mit 5 Einträgen und identischem
|
|
2990
|
+
* sichtbarem Text „Inhalt“ plus identischem `aria-label`
|
|
2991
|
+
* (`docs/index.html:7948–7960`, Doku-Sektion mit Platzhalter-Hrefs `#gt-article-toc`;
|
|
2992
|
+
* `docs/index.html:15049–15061`, reale Beispielseite Wissensbeitrag · KI mit echten
|
|
2993
|
+
* Abschnittsankern wie `#wb-ki-demo`).
|
|
2994
|
+
*
|
|
2995
|
+
* **Element-Selektor, ADR-0008-Standardfall.** `.article-toc` sitzt in beiden
|
|
2996
|
+
* Vorkommen als gewöhnlicher Block-Nachfahre direkt in `.article-body`
|
|
2997
|
+
* (`docs/index.html:7950`, `15051` je eine Ebene über dem `<details>`) — kein
|
|
2998
|
+
* Grid-/Flex-Kind, keine Layout-Klasse (`col-*`) vom Konsumenten, kein
|
|
2999
|
+
* Geschwister-Kombinator (`.article-toc` kommt in keiner CSS-Regel mit `+`/`~`
|
|
3000
|
+
* vor), kein Tag-Wechsel (immer `<details>`). Keines der drei ADR-0008-Kriterien
|
|
3001
|
+
* greift; ein Attributselektor brächte hier nichts. Die einzige direkte
|
|
3002
|
+
* Kind-Regel von `.article-body` (`.article-body > p`, css/components.css:1566)
|
|
3003
|
+
* zielt auf `p`, nicht auf `details` — ein `<cds-article-toc>`-Host zwischen
|
|
3004
|
+
* `.article-body` und dem `<details>` bricht deshalb keine Selektorkette.
|
|
3005
|
+
*
|
|
3006
|
+
* **Natives `<details>`/`<summary>`, kein nachgebautes Disclosure** (Ticket-Vorgabe,
|
|
3007
|
+
* Akzeptanzkriterium): Tastaturbedienung (Enter/Space auf `<summary>`) und
|
|
3008
|
+
* Toggle-Zustand kommen vollständig vom Browser, das CSS hängt direkt an `[open]`
|
|
3009
|
+
* (`.article-toc[open] .article-toc-caret`, css/components.css:1583). `<details
|
|
3010
|
+
* class="article-toc">` sitzt deshalb im TEMPLATE, nicht am Host — derselbe Grund
|
|
3011
|
+
* wie bei `cds-compare`s `<details class="ep-compare">` (`compare.component.ts`):
|
|
3012
|
+
* ein `<cds-article-toc>`-Host ist ein unbekanntes Custom Element und kann native
|
|
3013
|
+
* Disclosure-Semantik nicht annehmen.
|
|
3014
|
+
*
|
|
3015
|
+
* **Zweites Vorkommen des `<details>`-mit-drehendem-Caret-Musters, nicht
|
|
3016
|
+
* zusammengezogen mit `cds-compare` (ADR-0007 §5).** Beide Bauteile teilen die
|
|
3017
|
+
* Form (natives `<details>`, SVG-Caret, `[open]`-Rotation über
|
|
3018
|
+
* `transition:transform`), aber nicht die Aufgabe: `cds-compare` trägt eine
|
|
3019
|
+
* Vergleichstabelle mit `columns`/`rows`/`toggled`-Output, dieses Bauteil trägt
|
|
3020
|
+
* eine reine Link-Liste ohne Output (siehe API-Vorgabe des Tickets, die für dieses
|
|
3021
|
+
* Bauteil bewusst keinen `toggled` vorsieht). Zwei gleiche Stellen sind ein
|
|
3022
|
+
* Zufall, kein Muster — eine gemeinsame Basisklasse für zwei Vorkommen hätte nur
|
|
3023
|
+
* das `<details>`+Caret-Skelett gebündelt und dabei entweder Verhalten
|
|
3024
|
+
* vereinheitlicht (ein erzwungener `toggled`-Output ohne Zweck hier) oder so viel
|
|
3025
|
+
* parametrisiert, dass nichts gewonnen wäre.
|
|
3026
|
+
*
|
|
3027
|
+
* **`aria-label` am `<summary>` ist fest verdrahtet, kein Input.** Ausgezählt:
|
|
3028
|
+
* beide realen Vorkommen tragen wortgleich
|
|
3029
|
+
* `aria-label="Inhaltsverzeichnis ein- und ausklappen"` — der sichtbare Text
|
|
3030
|
+
* „Inhalt“ (`summary`-Input) allein wäre außerhalb des Beitragskontexts nicht
|
|
3031
|
+
* eindeutig, das `aria-label` schreibt deshalb aus, was das Element tut. Genau
|
|
3032
|
+
* dieser Wortlaut aus dem Mockup übernommen, kein erfundener zugänglicher Name.
|
|
3033
|
+
*
|
|
3034
|
+
* **Keine Nummerierung im Markup.** Die sichtbaren Ziffern vor jedem Eintrag
|
|
3035
|
+
* kommen aus `counter(toc)` auf `.article-toc-list li::before`
|
|
3036
|
+
* (css/components.css:1586), nicht aus dem `<ol>`-Default (`list-style:none` setzt
|
|
3037
|
+
* ihn zurück) — die Komponente muss dafür nichts weiter tun als `<ol
|
|
3038
|
+
* class="article-toc-list">` zu rendern.
|
|
3039
|
+
*/
|
|
3040
|
+
declare class ArticleTocComponent {
|
|
3041
|
+
/** Sichtbarer Text im `<summary>`, z. B. „Inhalt“. */
|
|
3042
|
+
readonly summary: _angular_core.InputSignal<string>;
|
|
3043
|
+
/** Anfangszustand des `<details>` — reine Vorbelegung, kein Zwei-Wege-Zustand. */
|
|
3044
|
+
readonly open: _angular_core.InputSignal<boolean>;
|
|
3045
|
+
/** Abschnitte des Beitrags, in Anzeige-Reihenfolge. */
|
|
3046
|
+
readonly items: _angular_core.InputSignal<CdsArticleTocItem[]>;
|
|
3047
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ArticleTocComponent, never>;
|
|
3048
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ArticleTocComponent, "cds-article-toc", never, { "summary": { "alias": "summary"; "required": false; "isSignal": true; }; "open": { "alias": "open"; "required": false; "isSignal": true; }; "items": { "alias": "items"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
/**
|
|
3052
|
+
* ArticleCallout (`cds-article-callout`) — Wrapper um `.article-callout*` aus
|
|
3053
|
+
* css/components.css (css/components.css:1591–1600): der bereichsgetönte
|
|
3054
|
+
* Aside-Block für Praxis-Beispiele und „In der Realität“-Einschübe im Lauftext
|
|
3055
|
+
* eines Wissensbeitrags. Zwölftes Ticket der Seitenbausteine-Serie
|
|
3056
|
+
* („Artikel-Körper“). Ausgezählt: 5 reale Vorkommen, je eines pro Bereich in der
|
|
3057
|
+
* Doku-Sektion (`docs/index.html:8086–8115`, co/ki/es/wo) plus eines in der realen
|
|
3058
|
+
* Beispielseite Wissensbeitrag · KI (`docs/index.html:15110–15114`).
|
|
3059
|
+
*
|
|
3060
|
+
* **Element-Selektor, ADR-0008-Standardfall.** `.article-callout` sitzt in allen 5
|
|
3061
|
+
* Vorkommen als gewöhnlicher Block-Nachfahre — die 4 Doku-Boxen je in einem
|
|
3062
|
+
* schlichten `<div>` innerhalb eines `display:flex;flex-direction:column`-Stapels
|
|
3063
|
+
* (kein Stretch-Bedarf in Spaltenrichtung), die reale Instanz direkt in
|
|
3064
|
+
* `.article-body`. Keines ist ein Grid-/Flex-Kind mit Streckungsbedarf, keines
|
|
3065
|
+
* trägt eine `col-*`-Klasse, `.article-callout` kommt in keiner CSS-Regel mit
|
|
3066
|
+
* einem Geschwister-Kombinator (`+`/`~`) vor, das Tag variiert nicht (immer
|
|
3067
|
+
* `<aside>`). Keines der drei ADR-0008-Kriterien greift. Die einzige direkte
|
|
3068
|
+
* Kind-Regel von `.article-body` (`.article-body > p`, css/components.css:1566)
|
|
3069
|
+
* zielt auf `p`, nicht auf `aside` — ein `<cds-article-callout>`-Host zwischen
|
|
3070
|
+
* `.article-body` und dem `<aside>` bricht deshalb keine Selektorkette.
|
|
3071
|
+
*
|
|
3072
|
+
* **Projizierte Absätze bleiben direkte Kinder von `.article-callout`, kein
|
|
3073
|
+
* `<div>` um `<ng-content>`** (Ticket-Vorgabe: `.article-callout > p` ist ein
|
|
3074
|
+
* Kindselektor, css/components.css:1600). `<ng-content>` selbst fügt kein
|
|
3075
|
+
* DOM-Element ein — es verschiebt nur die vom Konsumenten geschriebenen
|
|
3076
|
+
* `<p>`-Elemente an ihre Stelle direkt im `<aside>`, eine Ebene unter dem
|
|
3077
|
+
* `<cds-article-callout>`-Host, aber ohne zusätzlichen Knoten dazwischen. Geprüft
|
|
3078
|
+
* im laufenden Storybook (Story „Interaktiv“, Play-Funktion):
|
|
3079
|
+
* `aside.article-callout > p` liefert sowohl die Eyebrow als auch jeden
|
|
3080
|
+
* projizierten Absatz, `:scope > *` zählt keine fremde Zwischenebene.
|
|
3081
|
+
*
|
|
3082
|
+
* **`eyebrow` ist Beiwerk (Default `''`), `area` ist Konfiguration mit
|
|
3083
|
+
* verteidigbarer Vorgabe (`'co'`).** Die Basisregel `.article-callout` selbst
|
|
3084
|
+
* (ohne `[data-area]`) rendert bereits exakt wie `[data-area="co"]`
|
|
3085
|
+
* (`background:var(--co-50)`, `border-left-color:var(--co-500)`, beide Werte
|
|
3086
|
+
* identisch an beiden Stellen, css/components.css:1591–1592) — `'co'` ist damit
|
|
3087
|
+
* keine erfundene Markenfarbe, sondern der reale CSS-Fallback ohne Attribut.
|
|
3088
|
+
* Anders als bei `cds-pill` (dessen `.pill` ohne `data-area` gar keine Füllfarbe
|
|
3089
|
+
* hat) gibt es hier keinen sinnvollen „kein Bereich“-Zustand.
|
|
3090
|
+
*
|
|
3091
|
+
* **Bekannter CSS-Befund, nicht im Wrapper geflickt (ADR-0001):**
|
|
3092
|
+
* `.article-callout-eyebrow` (Spezifität 0,1,0) verliert gegen
|
|
3093
|
+
* `.article-callout > p` (Spezifität 0,1,1, css/components.css:1600), weil die
|
|
3094
|
+
* Eyebrow selbst ein `<p>` UND ein direktes Kind von `.article-callout` ist.
|
|
3095
|
+
* Gemessen in rohem Markup ohne Angular (Playwright/Chromium,
|
|
3096
|
+
* `getComputedStyle`): die Eyebrow rendert in JEDEM Bereich mit `font-size:16px`
|
|
3097
|
+
* statt `12px` und `margin-bottom:0` statt `4px`; die Akzentfarbe geht zusätzlich
|
|
3098
|
+
* verloren, wenn kein bereichsspezifischer Override existiert (`co` und „kein
|
|
3099
|
+
* `data-area`“ zeigen `--tx-primary` statt `--co-700`). Diese Komponente
|
|
3100
|
+
* reproduziert exakt die Klassen und die Struktur des Mockups und zeigt deshalb
|
|
3101
|
+
* denselben, vorbestehenden Fehler wie rohes HTML — siehe
|
|
3102
|
+
* `.scratch/angular-seitenbausteine/issues/22-css-luecke-callout-eyebrow-spezifitaet.md`.
|
|
3103
|
+
*
|
|
3104
|
+
* **`aria-labelledby` auf `<aside>`, sobald `eyebrow` gesetzt ist — Zusatz zum
|
|
3105
|
+
* Mockup, keine CSS-Änderung.** `<aside>` hat implizit die Landmark-Rolle
|
|
3106
|
+
* `complementary`; ein Wissensbeitrag mit mehr als einem Callout hat damit
|
|
3107
|
+
* mehrere gleichnamige Landmarks ohne zugänglichen Namen. Gemessen mit
|
|
3108
|
+
* axe-core an rohem Markup ohne Angular (zwei `<aside class="article-callout">`
|
|
3109
|
+
* ohne Auszeichnung): `landmark-unique` schlägt fehl, unabhängig von dieser
|
|
3110
|
+
* Komponente — das Mockup selbst kennt diese Auszeichnung nicht (siehe
|
|
3111
|
+
* `.scratch/angular-seitenbausteine/issues/23-doku-luecke-callout-landmark-label.md`).
|
|
3112
|
+
* Die Eyebrow ist bereits ein prägnanter, vom Redakteur gepflegter Kurztext
|
|
3113
|
+
* genau für diesen Zweck (z. B. „In der Praxis“) — sie bekommt deshalb eine
|
|
3114
|
+
* generierte `id`, auf die `aria-labelledby` zeigt, sobald sie existiert. Ohne
|
|
3115
|
+
* Eyebrow bleibt das `<aside>` unbenannt wie im Mockup: ein zugänglicher Name
|
|
3116
|
+
* ließe sich sonst nur aus dem projizierten Absatztext raten, und das wäre eine
|
|
3117
|
+
* erfundene Bezeichnung, keine reale.
|
|
3118
|
+
*/
|
|
3119
|
+
declare class ArticleCalloutComponent {
|
|
3120
|
+
private readonly instanceId;
|
|
3121
|
+
/** Kicker über dem Absatztext (`.article-callout-eyebrow`), leer = keine Eyebrow. */
|
|
3122
|
+
readonly eyebrow: _angular_core.InputSignal<string>;
|
|
3123
|
+
/** Markenbereich → `data-area` (Hintergrund + Akzentfarbe), siehe Klassendoku. */
|
|
3124
|
+
readonly area: _angular_core.InputSignal<CdsArea>;
|
|
3125
|
+
/**
|
|
3126
|
+
* `id` der Eyebrow für `aria-labelledby`, nur wenn eine Eyebrow existiert —
|
|
3127
|
+
* siehe Klassendoku.
|
|
3128
|
+
*
|
|
3129
|
+
* @internal
|
|
3130
|
+
*/
|
|
3131
|
+
protected readonly eyebrowId: _angular_core.Signal<string | null>;
|
|
3132
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ArticleCalloutComponent, never>;
|
|
3133
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ArticleCalloutComponent, "cds-article-callout", never, { "eyebrow": { "alias": "eyebrow"; "required": false; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
3134
|
+
}
|
|
3135
|
+
|
|
3136
|
+
/**
|
|
3137
|
+
* ArticleFigure (`cds-article-figure`) — Wrapper um `.article-figure`/
|
|
3138
|
+
* `.article-figcaption` aus css/components.css (css/components.css:1573–1575):
|
|
3139
|
+
* das redaktionelle Inline-Bild mit optionaler Bildunterschrift im Lauftext eines
|
|
3140
|
+
* Wissensbeitrags. Zwölftes Ticket der Seitenbausteine-Serie („Artikel-Körper“).
|
|
3141
|
+
* Ausgezählt: 3 reale Vorkommen — Doku-Demo mit Caption
|
|
3142
|
+
* (`docs/index.html:8028–8031`), reales Body-Bild mit Caption in der Beispielseite
|
|
3143
|
+
* Wissensbeitrag · KI (`docs/index.html:15115–15118`, identischer Bild- und
|
|
3144
|
+
* Caption-Text wie die Doku-Demo) und ein dritter, captionsloser Sonderfall direkt
|
|
3145
|
+
* unter dem Article Header derselben Beispielseite (`docs/index.html:15040–15042`,
|
|
3146
|
+
* dazu unten mehr).
|
|
3147
|
+
*
|
|
3148
|
+
* **Element-Selektor, ADR-0008-Standardfall.** In allen 3 Vorkommen sitzt
|
|
3149
|
+
* `.article-figure` als gewöhnlicher Block-Nachfahre (zwei in `.article-body`,
|
|
3150
|
+
* eines direkt in einer eigenen `.ep-section`) — kein Grid-/Flex-Kind, keine
|
|
3151
|
+
* `col-*`-Klasse vom Konsumenten, kein Tag-Wechsel (immer `<figure>`), kein
|
|
3152
|
+
* Vorkommen von `.article-figure` mit einem Geschwister-Kombinator in
|
|
3153
|
+
* css/components.css. Die einzige direkte Kind-Regel von `.article-body`
|
|
3154
|
+
* (`.article-body > p`, css/components.css:1566) zielt auf `p`, nicht auf
|
|
3155
|
+
* `figure` — ein `<cds-article-figure>`-Host zwischen `.article-body` und dem
|
|
3156
|
+
* `<figure>` bricht deshalb keine Selektorkette.
|
|
3157
|
+
*
|
|
3158
|
+
* **`loading="lazy"` ist fest verdrahtet, kein Input — bewusst NUR der
|
|
3159
|
+
* In-Article-Fall, nicht der dritte Mockup-Beleg.** Ausgezählt tragen 2 der 3
|
|
3160
|
+
* Vorkommen `loading="lazy"` (die beiden Bilder MIT Caption, mittig im
|
|
3161
|
+
* Lauftext, „below the fold“ — genau der in `wissensbeitrag.mdx`, Abschnitt
|
|
3162
|
+
* „In-Article Figure“, dokumentierte Dos-Punkt). Das dritte Vorkommen
|
|
3163
|
+
* (`docs/index.html:15040–15042`) trägt stattdessen `loading="eager"
|
|
3164
|
+
* fetchpriority="high"` UND hat keine Caption — es ist das erste Bild direkt
|
|
3165
|
+
* unter dem Article Header (Kommentar im Mockup: „Hero-Bild auf Body-Breite …
|
|
3166
|
+
* identisches Bild wie Vorschaubild für Wiedererkennung“), sitzt außerhalb von
|
|
3167
|
+
* `.article-body` und bekommt seine 720-px-Breite über ein
|
|
3168
|
+
* Inline-`style="max-width:720px;margin:0 auto"` am Konsumenten, nicht über die
|
|
3169
|
+
* Komponente (Randbedingung 6 der Spec: Mockup-Inline-Styles wandern nicht in
|
|
3170
|
+
* den Wrapper). Die Ticket-API sieht dafür kein `loading`/`fetchpriority`-Input
|
|
3171
|
+
* vor; dieses Bauteil deckt deshalb den (häufigeren, dokumentierten) In-Body-Fall
|
|
3172
|
+
* ab. Für den Lead-Bild-Sonderfall bleibt `<figure class="article-figure">` roh
|
|
3173
|
+
* schreibbar oder — sofern die Semantik passt — `cds-hero-image` (Ticket 02) zu
|
|
3174
|
+
* prüfen.
|
|
3175
|
+
*
|
|
3176
|
+
* **`alt` und `caption` sind bewusst getrennte Pflicht-/Beiwerk-Inputs, keine
|
|
3177
|
+
* Prüfung auf inhaltliche Überschneidung.** `wissensbeitrag.mdx` dokumentiert die
|
|
3178
|
+
* beiden Rollen ausdrücklich als unterschiedliche Jobs
|
|
3179
|
+
* (Dont: „Alt-Text und Caption inhaltlich identisch, Screenreader liest
|
|
3180
|
+
* doppelt“) — Vorgabe an den Konsumenten, nicht etwas, das eine Wrapper-Komponente
|
|
3181
|
+
* zur Laufzeit sinnvoll erzwingen könnte.
|
|
3182
|
+
*/
|
|
3183
|
+
declare class ArticleFigureComponent {
|
|
3184
|
+
/** Bildquelle. */
|
|
3185
|
+
readonly src: _angular_core.InputSignal<string>;
|
|
3186
|
+
/** Faktische Bildbeschreibung für Screenreader, siehe Klassendoku. */
|
|
3187
|
+
readonly alt: _angular_core.InputSignal<string>;
|
|
3188
|
+
/** Redaktionelle Bildunterschrift (`.article-figcaption`), leer = keine Caption. */
|
|
3189
|
+
readonly caption: _angular_core.InputSignal<string>;
|
|
3190
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ArticleFigureComponent, never>;
|
|
3191
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ArticleFigureComponent, "cds-article-figure", never, { "src": { "alias": "src"; "required": true; "isSignal": true; }; "alt": { "alias": "alt"; "required": true; "isSignal": true; }; "caption": { "alias": "caption"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
3192
|
+
}
|
|
3193
|
+
|
|
3194
|
+
/**
|
|
3195
|
+
* ArticlePullquote (`cds-article-pullquote`) — Wrapper um `.article-pullquote` aus
|
|
3196
|
+
* css/components.css (css/components.css:1608–1612): die typografische
|
|
3197
|
+
* Hervorhebung eines Satzes aus dem eigenen Lauftext eines Wissensbeitrags.
|
|
3198
|
+
* Zwölftes Ticket der Seitenbausteine-Serie („Artikel-Körper“). Ausgezählt: 2
|
|
3199
|
+
* reale Vorkommen, wortgleich, beide `data-area="ki"` — Doku-Demo
|
|
3200
|
+
* (`docs/index.html:8136`) und reale Beispielseite Wissensbeitrag · KI
|
|
3201
|
+
* (`docs/index.html:15075`).
|
|
3202
|
+
*
|
|
3203
|
+
* **Nicht `cds-blockquote` (`BlockquoteComponent`, `.bq`) — Ticket-Vorgabe, im
|
|
3204
|
+
* JSDoc beider Bauteile wechselseitig abgegrenzt.** Pull-Quote und Blockquote
|
|
3205
|
+
* teilen die typografische Familie (Serif, bereichsgefärbter Linksakzent in
|
|
3206
|
+
* `{area}-500`), aber nicht den Zweck, dokumentiert in `wissensbeitrag.mdx`,
|
|
3207
|
+
* Abschnitt „Pull-Quote gegen Blockquote“:
|
|
3208
|
+
*
|
|
3209
|
+
* | | Pull-Quote (dieses Bauteil) | Blockquote (`cds-blockquote`) |
|
|
3210
|
+
* |---|---|---|
|
|
3211
|
+
* | Wessen Stimme? | Der Beitrag zitiert sich selbst — ein Schlüsselsatz aus dem eigenen Lauftext | Eine dritte, benannte Person (Kund:in, Forschungsstimme, Branchen-Statement) |
|
|
3212
|
+
* | Attribution | Keine (`quote` ist der einzige Input) | Pflicht (`name`, `roleLabel`) |
|
|
3213
|
+
* | Hintergrund/Icon | Keiner — `quotes:none`, kein Quote-Icon, bewusst „offen“ statt umrahmt | Getönte Akzent-Box (`--bq-bg`) mit Quote-Icon (`.bq-icon`) |
|
|
3214
|
+
* | Tag | `<blockquote class="article-pullquote">` direkt, kein umschließendes `<figure>` | `<figure class="bq">` außen, `<blockquote>` (ohne eigene Klasse) innen für den Zitattext |
|
|
3215
|
+
*
|
|
3216
|
+
* Ein Self-Quote im eigenen Beitrag (Blockquote-Optik auf einem Satz des
|
|
3217
|
+
* Autors) wirkt sonst absurd — der Autor zitiert die Person, die den Beitrag
|
|
3218
|
+
* schreibt. Wer eine externe Stimme mit Namen und Rolle zeigen will, verwendet
|
|
3219
|
+
* `cds-blockquote`.
|
|
3220
|
+
*
|
|
3221
|
+
* **`quote` enthält die deutschen Anführungszeichen bereits als Teil des Texts,
|
|
3222
|
+
* die Komponente ergänzt keine.** `.article-pullquote{quotes:none}`
|
|
3223
|
+
* (css/components.css:1608) — es gibt keine `content:open-quote`/`close-quote`-Regel
|
|
3224
|
+
* in der CSS-Schicht, weder hier noch bei `.bq blockquote` oder
|
|
3225
|
+
* `.testimonial blockquote`. Ausgezählt stehen die Anführungszeichen in BEIDEN
|
|
3226
|
+
* realen Vorkommen als literale Zeichen im Text
|
|
3227
|
+
* („KI scheitert selten an der Technologie. …“), nicht CSS-generiert. Wie bei
|
|
3228
|
+
* `cds-compare`/`cds-table` ist Copy-Text Sache des Konsumenten (ADR-0007 §2) —
|
|
3229
|
+
* die Story liefert deshalb `quote` bereits mit „…“ (CONTRIBUTING.md §6), die
|
|
3230
|
+
* Komponente rendert den String unverändert, ohne eigene Anführungszeichen zu
|
|
3231
|
+
* erfinden.
|
|
3232
|
+
*
|
|
3233
|
+
* **Element-Selektor, ADR-0008-Standardfall.** `.article-pullquote` sitzt in
|
|
3234
|
+
* beiden Vorkommen als gewöhnlicher Block-Nachfahre in `.article-body` — kein
|
|
3235
|
+
* Grid-/Flex-Kind, keine `col-*`-Klasse vom Konsumenten, kein
|
|
3236
|
+
* Geschwister-Kombinator in css/components.css, kein Tag-Wechsel (immer
|
|
3237
|
+
* `<blockquote>`). Die einzige direkte Kind-Regel von `.article-body`
|
|
3238
|
+
* (`.article-body > p`, css/components.css:1566) zielt auf `p`, nicht auf
|
|
3239
|
+
* `blockquote` — ein `<cds-article-pullquote>`-Host zwischen `.article-body` und
|
|
3240
|
+
* dem `<blockquote>` bricht deshalb keine Selektorkette.
|
|
3241
|
+
*
|
|
3242
|
+
* **`area` ist Konfiguration mit verteidigbarer Vorgabe (`'co'`), keine
|
|
3243
|
+
* erfundene Bereichsfarbe.** Die Basisregel `.article-pullquote` selbst (ohne
|
|
3244
|
+
* `[data-area]`) setzt bereits exakt denselben Wert wie `[data-area="co"]`
|
|
3245
|
+
* (`border-left-color:var(--co-500)`, css/components.css:1608–1609) — `'co'` ist
|
|
3246
|
+
* damit der reale CSS-Fallback ohne Attribut, nicht geraten.
|
|
3247
|
+
*/
|
|
3248
|
+
declare class ArticlePullquoteComponent {
|
|
3249
|
+
/** Hervorgehobener Satz, inklusive deutscher Anführungszeichen, siehe Klassendoku. */
|
|
3250
|
+
readonly quote: _angular_core.InputSignal<string>;
|
|
3251
|
+
/** Markenbereich → `data-area` (Linksakzent), siehe Klassendoku. */
|
|
3252
|
+
readonly area: _angular_core.InputSignal<CdsArea>;
|
|
3253
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ArticlePullquoteComponent, never>;
|
|
3254
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ArticlePullquoteComponent, "cds-article-pullquote", never, { "quote": { "alias": "quote"; "required": true; "isSignal": true; }; "area": { "alias": "area"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
3255
|
+
}
|
|
3256
|
+
|
|
3257
|
+
/**
|
|
3258
|
+
* AuthorCard (`div[cdsAuthorCard]`) — Wrapper um `.author-card` aus css/components.css
|
|
3259
|
+
* (css/components.css:1602–1606): der Avatar-plus-Bio-Strip am Ende eines
|
|
3260
|
+
* Wissensbeitrags, einzeln oder mehrfach in `cds-author-card-group`. Dreizehntes und
|
|
3261
|
+
* letztes Ticket der Seitenbausteine-Serie. Ausgezählt: 17 reale Vorkommen in
|
|
3262
|
+
* `docs/index.html` — 4 einzelstehend (2 Doku-Demos `docs/index.html:8193,8207`, 1
|
|
3263
|
+
* Artikel-Demo `8424`, 1 reale Beispielseite `15179`) und 13 in fünf Gruppen (siehe
|
|
3264
|
+
* `AuthorCardGroupComponent`).
|
|
3265
|
+
*
|
|
3266
|
+
* **Attributselektor (ADR-0008), gemessen im `is-grid`-Fall statt nur hergeleitet.**
|
|
3267
|
+
* `.author-card-group.is-grid` (css/components.css:1555) ist `display:grid` ohne
|
|
3268
|
+
* eigenes `align-items` — der CSS-Default `stretch` gleicht die Kartenhöhen einer
|
|
3269
|
+
* Reihe deshalb NUR aus, wenn `.author-card` selbst das Grid-Kind ist. Spike-Story
|
|
3270
|
+
* (zwei Karten, 600-px-Raster, kurze gegen deutlich längere Bio, seither wieder
|
|
3271
|
+
* gelöscht) hat beide Varianten nebeneinander gebaut und mit `getBoundingClientRect()`
|
|
3272
|
+
* gemessen:
|
|
3273
|
+
*
|
|
3274
|
+
* | Variante | Grid-Kind (Tag) | `.author-card`-Höhen | Bio-Rect (kurz / lang) |
|
|
3275
|
+
* |---|---|---|---|
|
|
3276
|
+
* | Element-Selektor `<cds-author-card-naive>` | `CDS-AUTHOR-CARD-NAIVE`, 204/204 px (gestreckt) | **84 / 204 px** (ungleich) | 24×73 / 144×200 px |
|
|
3277
|
+
* | Attributselektor `div[cdsAuthorCardSpike]` | `DIV.author-card`, 204/204 px | **204 / 204 px** (gleich) | — |
|
|
3278
|
+
*
|
|
3279
|
+
* Beim Element-Selektor wird der UNSICHTBARE `<cds-author-card-naive>`-Host auf 204 px
|
|
3280
|
+
* gestreckt, die sichtbare `.author-card`-Box eine Ebene darunter bleibt bei ihrer
|
|
3281
|
+
* Inhaltshöhe (84 px) — exakt der Fehler aus ADR-0008 Fall 1. Mit dem Attributselektor
|
|
3282
|
+
* IST `.author-card` das Grid-Kind, `align-items:stretch` trifft direkt zu.
|
|
3283
|
+
*
|
|
3284
|
+
* **Ehrlicher Zusatz, anders als bei `cds-icon-card`: die Baseline-Screenshots beider
|
|
3285
|
+
* Spike-Varianten waren byte-identisch** (`sha1` gleich). `.author-card` trägt (anders
|
|
3286
|
+
* als `.ep-card`) weder Hintergrund noch Rahmen noch Schatten — der zusätzliche,
|
|
3287
|
+
* unsichtbare Leerraum unter der kurzen Bio malt in keiner der beiden Varianten ein
|
|
3288
|
+
* Pixel. Der Fehler ist damit heute rein strukturell (falsches Boxmodell, `getBoundingClientRect()`
|
|
3289
|
+
* auf `.author-card` lügt), nicht optisch sichtbar. Er wird real, sobald `.author-card`
|
|
3290
|
+
* künftig einen Rahmen/Hintergrund/Hover-Zustand bekommt, und er verfälscht schon
|
|
3291
|
+
* heute jede Messung/Positionierung, die sich auf die tatsächliche Kartenhöhe verlässt
|
|
3292
|
+
* (u. a. die Story „Als Raster“ unten, die genau das pinnt). Das ADR-0008-Kriterium
|
|
3293
|
+
* ist strukturell formuliert („die Stelle im DOM einnehmen, die es ohne Angular
|
|
3294
|
+
* einnähme“), nicht optisch — der Attributselektor bleibt deshalb die richtige Wahl,
|
|
3295
|
+
* obwohl der Effekt heute unsichtbar ist.
|
|
3296
|
+
*
|
|
3297
|
+
* **`area` ist bewusst NICHT Teil der API — CSS-Befund, nicht erfundene Konfiguration.**
|
|
3298
|
+
* Alle 17 realen `.author-card`-Vorkommen tragen `data-area`, und sowohl
|
|
3299
|
+
* `docs/index.html:8185` als auch `wissensbeitrag.mdx:199` behaupten wörtlich, das
|
|
3300
|
+
* färbe „nur den Avatar“. Ausgezählt gibt es aber in `css/components.css` (Light UND
|
|
3301
|
+
* Dark) KEINE einzige `.author-card[data-area="…"]`-Regel — weder direkt noch über
|
|
3302
|
+
* einen Nachfahren-Selektor auf `.article-avatar`, dessen eigene
|
|
3303
|
+
* `[data-area]`-Färbung (css/components.css:1523–1526) ein Element-Selektor auf dem
|
|
3304
|
+
* Avatar selbst ist, unabhängig vom Vorfahren. `data-area` auf `.author-card` ist
|
|
3305
|
+
* damit, gegen die Doku-Aussage, komplett wirkungslos — die Bereichsfarbe kommt
|
|
3306
|
+
* ausschließlich vom `area`-Input, den der Konsument direkt an das projizierte
|
|
3307
|
+
* `[cdsAvatar]` bindet (siehe unten). Ein `area`-Input, der hier nur `data-area` ohne
|
|
3308
|
+
* jede CSS-Wirkung setzt, wäre eine erfundene Konfiguration ohne Gegenwert — Befund
|
|
3309
|
+
* gemeldet, nicht im Wrapper geflickt (ADR-0001):
|
|
3310
|
+
* `.scratch/angular-seitenbausteine/issues/24-css-luecke-author-card-data-area.md`.
|
|
3311
|
+
*
|
|
3312
|
+
* **Avatar projiziert über `[cdsAvatar]`, wie bei `cds-article-header`.** Der
|
|
3313
|
+
* Konsument setzt Größe (`size="lg"`, css/components.css:1527) und Bereich
|
|
3314
|
+
* (`area="…"`) direkt am projizierten `<div cdsAvatar>` — diese Komponente reicht
|
|
3315
|
+
* nichts durch, exakt die Trennung, die `cds-article-header` bereits etabliert
|
|
3316
|
+
* (siehe dessen Klassendoku, Abschnitt „Avatar projiziert über `[cdsAvatar]`“).
|
|
3317
|
+
*
|
|
3318
|
+
* **Eyebrow als `<h3>`, nur wenn gesetzt** (Beiwerk, Default `''`) — deckungsgleich
|
|
3319
|
+
* mit allen 4 einzelstehenden realen Vorkommen: 2 tragen `<h3 class="author-card-eyebrow">`
|
|
3320
|
+
* (`docs/index.html:8196,8427`, „Über den Autor“/„Über die Autorin“, Ticket-Vorgabe:
|
|
3321
|
+
* eine Ebene unter dem `<h2>` des Article-Body), die anderen 2 (Doku-Demos ohne
|
|
3322
|
+
* umgebende Artikelstruktur) haben gar keinen. Innerhalb einer Gruppe trägt laut
|
|
3323
|
+
* `wissensbeitrag.mdx` („Eyebrow-Konvention“) keine einzelne Karte mehr einen eigenen
|
|
3324
|
+
* Eyebrow — die Gruppen-Überschrift ersetzt sie (siehe `AuthorCardGroupComponent`);
|
|
3325
|
+
* Konsumenten lassen `eyebrow` dafür einfach leer.
|
|
3326
|
+
*
|
|
3327
|
+
* **`roleLabel`, NICHT `role` wie im Ticket-Text — gemessene Abweichung, kein
|
|
3328
|
+
* Freihand-Entscheid.** Ein erster Entwurf folgte der Ticket-API wörtlich
|
|
3329
|
+
* (`role`-Input, mit `role="…"` als schlichtem Attribut in den Stories gesetzt, wie
|
|
3330
|
+
* `name`/`bio`/`eyebrow`). Storybooks eingebauter a11y-Check (`aria-roles`) schlug
|
|
3331
|
+
* darauf für JEDE Story mit echtem Rollentext fehl: `role` ist ein globales
|
|
3332
|
+
* HTML-/ARIA-Attribut, und ein Angular-Input MIT DIESEM NAMEN wird bei einem
|
|
3333
|
+
* ungebundenen `role="Senior AI Engineer …"` zwar als Startwert an den Input
|
|
3334
|
+
* durchgereicht, bleibt aber ZUSÄTZLICH als echtes `role`-Attribut im DOM stehen —
|
|
3335
|
+
* axe meldete wörtlich „Roles must be one of the valid ARIA roles: Senior, AI,
|
|
3336
|
+
* Engineer, …“, weil der Freitext als ARIA-Rolle interpretiert wird. Exakt dieselbe
|
|
3337
|
+
* Kollision umgehen `BlockquoteComponent`, `TestimonialComponent` und
|
|
3338
|
+
* `TeamVoiceComponent` bereits mit `roleLabel` statt `role`
|
|
3339
|
+
* (`grep -rn "readonly role" angular-lib/projects/design-system-angular/src/lib/`
|
|
3340
|
+
* zeigt nur diese drei). Diese Komponente folgt demselben, bereits etablierten
|
|
3341
|
+
* Muster statt die Kollision ein viertes Mal zu wiederholen — CSS-Klasse bleibt
|
|
3342
|
+
* unverändert `.author-card-role`, nur der Input-Name weicht vom Ticket-Text ab.
|
|
3343
|
+
*/
|
|
3344
|
+
declare class AuthorCardComponent {
|
|
3345
|
+
/** Name (`.author-card-name`). */
|
|
3346
|
+
readonly name: _angular_core.InputSignal<string>;
|
|
3347
|
+
/**
|
|
3348
|
+
* Rolle/Position (`.author-card-role`), leer = keine Rollenzeile. Heißt bewusst
|
|
3349
|
+
* `roleLabel`, nicht `role` (Kollision mit dem ARIA-Attribut `role`), siehe
|
|
3350
|
+
* Klassendoku.
|
|
3351
|
+
*/
|
|
3352
|
+
readonly roleLabel: _angular_core.InputSignal<string>;
|
|
3353
|
+
/** Bio-Text (`.author-card-bio`), leer = kein Bio-Absatz. */
|
|
3354
|
+
readonly bio: _angular_core.InputSignal<string>;
|
|
3355
|
+
/** Kicker über dem Namen (`.author-card-eyebrow`, `<h3>`), leer = keine Eyebrow. */
|
|
3356
|
+
readonly eyebrow: _angular_core.InputSignal<string>;
|
|
3357
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<AuthorCardComponent, never>;
|
|
3358
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<AuthorCardComponent, "div[cdsAuthorCard]", never, { "name": { "alias": "name"; "required": true; "isSignal": true; }; "roleLabel": { "alias": "roleLabel"; "required": false; "isSignal": true; }; "bio": { "alias": "bio"; "required": false; "isSignal": true; }; "eyebrow": { "alias": "eyebrow"; "required": false; "isSignal": true; }; }, {}, never, ["[cdsAvatar]"], true, never>;
|
|
3359
|
+
}
|
|
3360
|
+
|
|
3361
|
+
/**
|
|
3362
|
+
* AuthorCardGroup (`cds-author-card-group`) — Wrapper um `.author-card-group` aus
|
|
3363
|
+
* css/components.css (css/components.css:1547–1563): bündelt mehrere `[cdsAuthorCard]`
|
|
3364
|
+
* am Ende eines Wissensbeitrags, gestapelt (Default) oder als zweispaltiges Raster
|
|
3365
|
+
* (`is-grid`, css/components.css:1555). Dreizehntes und letztes Ticket der
|
|
3366
|
+
* Seitenbausteine-Serie. Ausgezählt: 5 reale Gruppen — 2 gestapelt in der
|
|
3367
|
+
* Artikel-Demo (`docs/index.html:8440,8465`, je 2 bzw. 3 Karten), 2 `is-grid`
|
|
3368
|
+
* (Doku-Demo `9112`, reale Beispielseite Scrum-Training `13811`, je 4 Karten) und 0
|
|
3369
|
+
* `is-grid`-Vorkommen im Wissensbeitrag selbst — `wissensbeitrag.mdx` verweist für
|
|
3370
|
+
* `is-grid` ausdrücklich auf `Seitenmuster/Seminar · Training`, „weil sie dort und
|
|
3371
|
+
* nicht in Wissensbeiträgen vorkommt“.
|
|
3372
|
+
*
|
|
3373
|
+
* **Element-Selektor, ADR-0008-Standardfall FÜR DIE GRUPPE SELBST** — anders als die
|
|
3374
|
+
* projizierte Karte. `.author-card-group` ist in keinem der 5 realen Vorkommen
|
|
3375
|
+
* selbst ein Grid-/Flex-Kind mit Streckungsbedarf (jedes sitzt direkt in einer
|
|
3376
|
+
* `.ep-section`/einem einfachen `<div>`), trägt nie eine `col-*`-Klasse vom
|
|
3377
|
+
* Konsumenten und kommt in keiner CSS-Regel mit einem Geschwister-Kombinator vor. Das
|
|
3378
|
+
* Tag variiert nicht (immer `<div>`). Ein `<cds-author-card-group>`-Host darüber
|
|
3379
|
+
* bricht deshalb selbst keine Selektorkette — die Karten dagegen brauchen ihren
|
|
3380
|
+
* eigenen Attributselektor, siehe `AuthorCardComponent`.
|
|
3381
|
+
*
|
|
3382
|
+
* **Die Klasse `.author-card-group` sitzt trotzdem NICHT auf dem Host, sondern auf
|
|
3383
|
+
* einem inneren `<div>` — das ist eine eigene, von den Karten unabhängige
|
|
3384
|
+
* Randbedingung.** Grund: `.author-card-group-eyebrow:has(+ .author-card-group.is-grid)`
|
|
3385
|
+
* (css/components.css:1563) ist ein Geschwister-Selektor: die Eyebrow-Überschrift
|
|
3386
|
+
* muss ein UNMITTELBARES Geschwister von `.author-card-group` sein, damit sie im
|
|
3387
|
+
* `is-grid`-Fall die breitere `max-width:960px` statt der Basis-`720px`
|
|
3388
|
+
* (css/components.css:1560) bekommt — sonst „sitzt sie bei der Raster-Variante 120 px
|
|
3389
|
+
* links neben der ersten Karte“ (Kommentar im CSS selbst). Läge `.author-card-group`
|
|
3390
|
+
* auf dem Host, könnte diese Komponente die Eyebrow nicht als Geschwister VOR dem
|
|
3391
|
+
* Host rendern (ein Angular-Template füllt ausschließlich das Innere seines eigenen
|
|
3392
|
+
* Hosts, nie dessen Umgebung) — die Eyebrow müsste dann ALS KIND in `.author-card-group`
|
|
3393
|
+
* selbst stehen und würde im Raster zur eigenen Grid-Zelle neben der ersten Karte,
|
|
3394
|
+
* sichtbar falsch. Mit der Klasse auf einem inneren `<div>` rendert das Template
|
|
3395
|
+
* `<h3 class="author-card-group-eyebrow">` und `<div class="author-card-group …">`
|
|
3396
|
+
* als echte, unmittelbare Geschwister IM Host `<cds-author-card-group>` — der
|
|
3397
|
+
* CSS-Selektor sieht nur diese beiden Geschwister, der zusätzliche äußere Host
|
|
3398
|
+
* ändert daran nichts, weil `:has(+ …)` ausschließlich den unmittelbaren
|
|
3399
|
+
* Elternknoten der beiden betrachtet, nicht dessen Vorfahren. Ohne diese zweite
|
|
3400
|
+
* Ebene bräuchte die Eyebrow denselben Attributselektor-Trick wie die Karte, obwohl
|
|
3401
|
+
* `.author-card-group` selbst kein Grid-/Flex-Kind ist — das eigentliche Problem hier
|
|
3402
|
+
* ist ein Geschwister-, kein Eltern-Kind-Kriterium, deshalb reicht die einfachere
|
|
3403
|
+
* Lösung (innere Klasse statt Host-Klasse).
|
|
3404
|
+
*
|
|
3405
|
+
* **Direkte Kinder bleiben gewahrt, `is-grid` inklusive.** `<ng-content>` fügt kein
|
|
3406
|
+
* eigenes Element ein: die projizierten `div[cdsAuthorCard]` (bereits selbst
|
|
3407
|
+
* `.author-card` am eigenen Host, siehe dort) landen unverändert als direkte Kinder
|
|
3408
|
+
* des inneren `.author-card-group`-`<div>` — Voraussetzung für den
|
|
3409
|
+
* Höhenausgleich im `is-grid`-Fall (`AuthorCardComponent`) und für den
|
|
3410
|
+
* Nachfahren-Selektor `.author-card-group .author-card` (css/components.css:1548).
|
|
3411
|
+
*
|
|
3412
|
+
* **`eyebrow` als `<h3>`, deckungsgleich mit den beiden realen, VOLLSTÄNDIGEN
|
|
3413
|
+
* Seiten-Vorkommen der gestapelten Variante** (`docs/index.html:8439,8464`, „Über die
|
|
3414
|
+
* Autor:innen“) — nicht mit der isolierten Doku-Illustration (`9111`, `<p>`,
|
|
3415
|
+
* dokumentiert ohne umgebende `<h2>`-Hierarchie). Die einzige reale `is-grid`-Seite
|
|
3416
|
+
* (Scrum-Training, `13811`) verzichtet auf die Gruppen-Eyebrow komplett, weil die
|
|
3417
|
+
* Sektion bereits `.ep-section-label` plus `<h2>` trägt — exakt der in
|
|
3418
|
+
* `wissensbeitrag.mdx` („Trägt die Sektion bereits ein `.ep-section-label` plus H2,
|
|
3419
|
+
* entfällt sie“) dokumentierte Fall. Konsumenten mit eigener Sektionsüberschrift
|
|
3420
|
+
* lassen `eyebrow` dafür leer, statt eine zweite `<h3>` zu erzwingen.
|
|
3421
|
+
*
|
|
3422
|
+
* **Konsequenz der inneren Klasse: der Host `<cds-author-card-group>` selbst trägt
|
|
3423
|
+
* weder `.author-card-group` noch `.is-grid`.** Ein `class`/`style` direkt am
|
|
3424
|
+
* `<cds-author-card-group>`-Tag landet auf dem unbekannten, standardmäßig
|
|
3425
|
+
* `display:inline` gerenderten Custom Element, NICHT auf der tatsächlichen
|
|
3426
|
+
* Layout-Box (dem inneren `<div>` mit `max-width`/Grid). Wer Layout-Eigenschaften
|
|
3427
|
+
* setzen will (z. B. `margin-top` wie im Trainer-Beispiel,
|
|
3428
|
+
* `docs/index.html:13811`), setzt sie am umgebenden Konsumenten-Markup, nicht am
|
|
3429
|
+
* Host — dieselbe Einschränkung, die ADR-0008 für Attributselektor-Komponenten
|
|
3430
|
+
* beschreibt, hier aber aus einem Geschwister- statt einem Eltern-Kind-Grund.
|
|
3431
|
+
*/
|
|
3432
|
+
declare class AuthorCardGroupComponent {
|
|
3433
|
+
/** Gemeinsame Überschrift (`.author-card-group-eyebrow`, `<h3>`), leer = keine. */
|
|
3434
|
+
readonly eyebrow: _angular_core.InputSignal<string>;
|
|
3435
|
+
/** Zweispaltiges Raster statt gestapelter Liste (`.is-grid`). */
|
|
3436
|
+
readonly grid: _angular_core.InputSignal<boolean>;
|
|
3437
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<AuthorCardGroupComponent, never>;
|
|
3438
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<AuthorCardGroupComponent, "cds-author-card-group", never, { "eyebrow": { "alias": "eyebrow"; "required": false; "isSignal": true; }; "grid": { "alias": "grid"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
3439
|
+
}
|
|
3440
|
+
|
|
3441
|
+
export { AreaBadgeComponent, AreaTabComponent, AreaTabsComponent, ArticleCalloutComponent, ArticleFigureComponent, ArticleHeaderComponent, ArticlePullquoteComponent, ArticleTocComponent, AuthorCardComponent, AuthorCardGroupComponent, AvatarComponent, AvatarStackComponent, BlockquoteComponent, ButtonComponent, CDS_AREAS, CDS_AREA_LABELS, CDS_THEME_ICON, CDS_THEME_LABEL, CDS_THEME_ORDER, CardComponent, CarouselComponent, CheckboxComponent, ChipComponent, CodeBlockComponent, ComboboxComponent, CompareComponent, CtaBandComponent, DownloadCtaComponent, FactsComponent, FaqComponent, FeatureComponent, FeaturedCardComponent, FooterBottomComponent, FooterComponent, FooterMainComponent, HeroImageComponent, IconCardComponent, LinkCardComponent, LogoCarouselComponent, LogoComponent, PillComponent, RadioGroupComponent, ScaleComponent, SectionComponent, SelectComponent, SelectFieldComponent, SliderComponent, SnackbarComponent, StatCardComponent, StatStripComponent, StatusBadgeComponent, StoererComponent, StoererSetComponent, TableComponent, TeamVoiceComponent, TestimonialComponent, TextFieldComponent, TextareaFieldComponent, ThemeCycleComponent, ThemeModeService, ThemeSegmentComponent, ThemeSelectComponent, TierComponent, TopnavComponent, cdsThemeModes, themeStore };
|
|
3442
|
+
export type { CdsArea, CdsArticleBreadcrumbItem, CdsArticleTocItem, CdsAvatarSize, CdsBadgeTone, CdsButtonSize, CdsButtonVariant, CdsCompareColumn, CdsCompareRow, CdsFactsItem, CdsFaqItem, CdsFlatStat, CdsFooterLink, CdsLogo, CdsNavItem, CdsNavSubItem, CdsSelectOption, CdsSlide, CdsSnackTone, CdsSocialLink, CdsSocialPlatform, CdsThemeMode };
|