@lifecorner/angular-ui-kit 3.31.0 → 3.33.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/fesm2022/lifecorner-angular-ui-kit-bottom-nav.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-bottom-nav.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-chat.mjs +21 -5
- package/fesm2022/lifecorner-angular-ui-kit-chat.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs +90 -21
- package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-datepicker.mjs +7 -6
- package/fesm2022/lifecorner-angular-ui-kit-datepicker.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-header.mjs +21 -4
- package/fesm2022/lifecorner-angular-ui-kit-header.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-internal-a11y.mjs +21 -1
- package/fesm2022/lifecorner-angular-ui-kit-internal-a11y.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-list.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-list.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-next-step-card.mjs +21 -4
- package/fesm2022/lifecorner-angular-ui-kit-next-step-card.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-select.mjs +5 -4
- package/fesm2022/lifecorner-angular-ui-kit-select.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-sidenav.mjs +4 -4
- package/fesm2022/lifecorner-angular-ui-kit-sidenav.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-table.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-table.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-tag-input.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-tag-input.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-textarea.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-textarea.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-toast.mjs +9 -4
- package/fesm2022/lifecorner-angular-ui-kit-toast.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-tooltip.mjs +54 -9
- package/fesm2022/lifecorner-angular-ui-kit-tooltip.mjs.map +1 -1
- package/package.json +1 -1
- package/src/styles/_theme-dark.scss +9 -0
- package/src/styles/_theme-light.scss +9 -0
- package/src/styles/theme-contrast.spec.ts +14 -6
- package/src/styles/theme-modes.spec.ts +44 -0
- package/types/lifecorner-angular-ui-kit-bottom-nav.d.ts +3 -2
- package/types/lifecorner-angular-ui-kit-chat.d.ts +14 -1
- package/types/lifecorner-angular-ui-kit-combobox.d.ts +40 -2
- package/types/lifecorner-angular-ui-kit-datepicker.d.ts +4 -1
- package/types/lifecorner-angular-ui-kit-header.d.ts +26 -0
- package/types/lifecorner-angular-ui-kit-internal-a11y.d.ts +12 -1
- package/types/lifecorner-angular-ui-kit-list.d.ts +6 -1
- package/types/lifecorner-angular-ui-kit-next-step-card.d.ts +15 -0
- package/types/lifecorner-angular-ui-kit-select.d.ts +7 -0
- package/types/lifecorner-angular-ui-kit-sidenav.d.ts +3 -1
- package/types/lifecorner-angular-ui-kit-toast.d.ts +14 -3
- package/types/lifecorner-angular-ui-kit-tooltip.d.ts +31 -2
|
@@ -486,3 +486,12 @@
|
|
|
486
486
|
.lc-header--light {
|
|
487
487
|
@include -light-theme-colors;
|
|
488
488
|
}
|
|
489
|
+
|
|
490
|
+
// `<lc-sidenav theme="light">` on a dark page: the rail is light, but its
|
|
491
|
+
// badges took the page's dark palette and measured 1.11–1.96:1 (request 051,
|
|
492
|
+
// 050-f2). Re-declared on the badge host they match a light page (4.85–10.97:1).
|
|
493
|
+
// Only the badge: the collapsed rail's label tooltip floats over the page and
|
|
494
|
+
// keeps the page palette. A root-level token override does not reach inside.
|
|
495
|
+
.lc-sidenav-container--light .lc-sidenav__nav-badge {
|
|
496
|
+
@include -light-theme-colors;
|
|
497
|
+
}
|
|
@@ -457,13 +457,18 @@ describe('Component pairings of 3.19.1 (request 025)', () => {
|
|
|
457
457
|
for (const rule of chipRules) expect(rule[4]).not.toMatch(/#[0-9a-f]{3,8}\b|rgba?\(|:\s*white\b/i);
|
|
458
458
|
});
|
|
459
459
|
|
|
460
|
-
|
|
460
|
+
// 3.33 (request 052, 044-n3, AC-16): the pressed mark in forced colours is
|
|
461
|
+
// a Highlight underline (the rich-text-editor's), not the focus ring's
|
|
462
|
+
// 2 px outline, and it stays while the chip is focused.
|
|
463
|
+
it('AC-13: in forced colours the pressed chip is underlined in Highlight, also while focused', () => {
|
|
461
464
|
const at = ncCss.indexOf('@media (forced-colors: active)');
|
|
462
465
|
expect(at).toBeGreaterThan(-1);
|
|
463
466
|
const media = ncCss.slice(at, ncCss.indexOf('}\n}', at));
|
|
464
|
-
expect(media).toMatch(
|
|
465
|
-
|
|
466
|
-
|
|
467
|
+
expect(media).toMatch(
|
|
468
|
+
/\.lc-nc__filter\[aria-pressed=true\] \{\s*text-decoration-line: underline;\s*text-decoration-thickness: 2px;\s*text-underline-offset: 4px;\s*text-decoration-color: Highlight;/,
|
|
469
|
+
);
|
|
470
|
+
expect(media).not.toMatch(/:not\(:focus-visible\)/);
|
|
471
|
+
expect(media).not.toMatch(/outline: 2px solid Highlight;/);
|
|
467
472
|
});
|
|
468
473
|
|
|
469
474
|
/** Design 025 §5, per palette [light, dark]: the minimum over both panel ends. */
|
|
@@ -735,12 +740,15 @@ describe('Component pairings of 3.21.1 (request 029)', () => {
|
|
|
735
740
|
expect(sheets.nc).not.toMatch(/#3b82f6|#22c55e|#f59e0b|#ef4444/i);
|
|
736
741
|
});
|
|
737
742
|
|
|
738
|
-
|
|
743
|
+
// 3.33 (request 052, 044-n3, AC-16): the forced-colours mark is the
|
|
744
|
+
// rich-text-editor's Highlight underline (was a 2 px outline, hidden on focus).
|
|
745
|
+
it('D-2: the level pills stay at full opacity; the pressed pill is underlined in forced colours', () => {
|
|
739
746
|
expect(fills('log', '.lc-log-viewer__level-pill').opacity).toBeUndefined();
|
|
740
747
|
expect(fills('log', '.lc-log-viewer__level-pill--active').opacity).toBeUndefined();
|
|
741
748
|
expect(sheets.log).toMatch(
|
|
742
|
-
/@media \(forced-colors: active\) \{\s*\.lc-log-viewer__level-pill
|
|
749
|
+
/@media \(forced-colors: active\) \{\s*\.lc-log-viewer__level-pill\[aria-pressed=true\] \{\s*text-decoration-line: underline;\s*text-decoration-thickness: 2px;\s*text-underline-offset: 4px;\s*text-decoration-color: Highlight;\s*\}/,
|
|
743
750
|
);
|
|
751
|
+
expect(sheets.log).not.toMatch(/level-pill--active:not\(:focus-visible\)/);
|
|
744
752
|
});
|
|
745
753
|
|
|
746
754
|
it('AC-10: the progress track is unchanged; primary reads the theme token (request 039), secondary the data mark (036)', () => {
|
|
@@ -98,3 +98,47 @@ describe('Theme scope of a themed header', () => {
|
|
|
98
98
|
expect(dark.indexOf('.lc-header--dark {')).toBeGreaterThan(dark.indexOf(':root.system {'));
|
|
99
99
|
});
|
|
100
100
|
});
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Request 051 (050-f2, design §1 B, D-3): a sidenav with `theme="light"` /
|
|
104
|
+
* `"dark"` re-declares every colour token of that theme on its badge hosts,
|
|
105
|
+
* so the badges take the rail's palette instead of the page's (1.11–1.96:1 on
|
|
106
|
+
* a light rail on a dark page before). Only the badge host: the collapsed
|
|
107
|
+
* rail's label tooltip keeps the page palette. On a matching page the values
|
|
108
|
+
* equal the root's, so the badge renders exactly as before (AC-4).
|
|
109
|
+
*/
|
|
110
|
+
describe('Theme scope of a themed sidenav rail badge (request 051)', () => {
|
|
111
|
+
const COLOUR = /^--(color-|gradient-|shadow-|elevation-|accent-line$|app-background-image$)/;
|
|
112
|
+
const colours = (tokens: Record<string, string>) =>
|
|
113
|
+
Object.fromEntries(Object.entries(tokens).filter(([name]) => COLOUR.test(name)));
|
|
114
|
+
const LIGHT_RAIL = '.lc-sidenav-container--light .lc-sidenav__nav-badge {';
|
|
115
|
+
const DARK_RAIL = '.lc-sidenav-container--dark .lc-sidenav__nav-badge {';
|
|
116
|
+
|
|
117
|
+
it('AC-3/AC-4: the light rail badge declares exactly the colour tokens of the light root (172), with the same values', () => {
|
|
118
|
+
const badge = tokensOf(light, `\n${LIGHT_RAIL}`);
|
|
119
|
+
expect(Object.keys(badge)).toHaveLength(172);
|
|
120
|
+
expect(badge).toEqual(colours(tokensOf(light, ':root,')));
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('AC-3/AC-4: the dark rail badge declares exactly the colour tokens of the dark root (172), with the same values', () => {
|
|
124
|
+
const badge = tokensOf(dark, `\n${DARK_RAIL}`);
|
|
125
|
+
expect(Object.keys(badge)).toHaveLength(172);
|
|
126
|
+
expect(badge).toEqual(colours(tokensOf(dark, ':root.dark,')));
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('emits each rail rule once, in its own theme file, after the header rule', () => {
|
|
130
|
+
expect(light.split(LIGHT_RAIL)).toHaveLength(2);
|
|
131
|
+
expect(dark.split(DARK_RAIL)).toHaveLength(2);
|
|
132
|
+
expect(light.includes('.lc-sidenav-container--dark')).toBe(false);
|
|
133
|
+
expect(dark.includes('.lc-sidenav-container--light')).toBe(false);
|
|
134
|
+
expect(light.indexOf(LIGHT_RAIL)).toBeGreaterThan(light.indexOf('.lc-header--light {'));
|
|
135
|
+
expect(dark.indexOf(DARK_RAIL)).toBeGreaterThan(dark.indexOf('.lc-header--dark {'));
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('scopes the badge host only, not the rail or its label tooltip', () => {
|
|
139
|
+
for (const css of [light, dark]) {
|
|
140
|
+
const selectors = [...css.matchAll(/(^|\n)([^\n{}]*lc-sidenav[^\n{}]*)\{/g)].map((m) => m[2].trim());
|
|
141
|
+
expect(selectors).toEqual([expect.stringMatching(/^\.lc-sidenav-container--(light|dark) \.lc-sidenav__nav-badge$/)]);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -136,8 +136,9 @@ export declare class BottomNavComponent {
|
|
|
136
136
|
* tasks on the pages behind it: drawn like an item badge and read after the
|
|
137
137
|
* label through `badgeAriaLabel`. The kit doesn't add up the hidden
|
|
138
138
|
* destinations' badges; pass the value to show. It sits on the bar's last
|
|
139
|
-
* entry
|
|
140
|
-
* badge
|
|
139
|
+
* entry; values up to three characters (`'99+'`) fit: on the last entry a
|
|
140
|
+
* badge that would pass the entry's edge moves left until it ends there
|
|
141
|
+
* (3.33). `null` or `undefined`: no badge.
|
|
141
142
|
*
|
|
142
143
|
* @default null
|
|
143
144
|
*/
|
|
@@ -128,7 +128,12 @@ export declare class ChatComponent {
|
|
|
128
128
|
multiple: import("@angular/core").InputSignal<boolean>;
|
|
129
129
|
/** Maximum file size in bytes. `0` disables the check. */
|
|
130
130
|
maxFileSize: import("@angular/core").InputSignal<number>;
|
|
131
|
-
/**
|
|
131
|
+
/**
|
|
132
|
+
* BCP 47 locale of the message times (hour and minute). Attachment sizes
|
|
133
|
+
* follow the host's language (closest `lang`, 3.33), English without one:
|
|
134
|
+
* `240,0 KB` under `lang="de"`.
|
|
135
|
+
* @default 'de-DE'
|
|
136
|
+
*/
|
|
132
137
|
locale: import("@angular/core").InputSignal<string>;
|
|
133
138
|
/** Header badge while a message is streaming. */
|
|
134
139
|
streamingLabel: import("@angular/core").InputSignal<string>;
|
|
@@ -211,7 +216,15 @@ export declare class ChatComponent {
|
|
|
211
216
|
protected openFilePicker(): void;
|
|
212
217
|
protected onFilesSelected(event: Event): void;
|
|
213
218
|
protected removeAttachment(id: string): void;
|
|
219
|
+
private readonly host;
|
|
220
|
+
/**
|
|
221
|
+
* Attachment size with one decimal in the host's language (3.33, request
|
|
222
|
+
* 052, 044-n2): the closest `lang`, English without one (or when it is
|
|
223
|
+
* empty or invalid), so `240.0 KB` by default and `240,0 KB` under
|
|
224
|
+
* `lang="de"`. No digit grouping, as before.
|
|
225
|
+
*/
|
|
214
226
|
protected formatBytes(size: number | undefined): string;
|
|
227
|
+
private decimalFormat;
|
|
215
228
|
protected isImage(att: ChatAttachment): boolean;
|
|
216
229
|
private addFiles;
|
|
217
230
|
private matchesAccept;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ControlValueAccessor } from "@angular/forms";
|
|
2
|
+
import { ConnectedPosition } from "@angular/cdk/overlay";
|
|
2
3
|
import { Observable } from "rxjs";
|
|
3
4
|
import * as i0 from "@angular/core";
|
|
4
5
|
export interface ComboboxOption {
|
|
@@ -42,6 +43,7 @@ export declare class ComboboxComponent implements ControlValueAccessor {
|
|
|
42
43
|
protected readonly createOptionId: string;
|
|
43
44
|
private readonly inputEl;
|
|
44
45
|
private readonly dropdownEl;
|
|
46
|
+
private readonly inputWrapper;
|
|
45
47
|
private readonly elRef;
|
|
46
48
|
private readonly querySubject;
|
|
47
49
|
/** Sync options */
|
|
@@ -76,6 +78,16 @@ export declare class ComboboxComponent implements ControlValueAccessor {
|
|
|
76
78
|
readonly createOptionLabel: import("@angular/core").InputSignal<string>;
|
|
77
79
|
/** Size */
|
|
78
80
|
readonly size: import("@angular/core").InputSignal<ComboboxSize>;
|
|
81
|
+
/**
|
|
82
|
+
* Whether the list opens when the field gets focus (3.32, request 051).
|
|
83
|
+
* With `false`, focus alone (`focus()` from the page, Tab) leaves the list
|
|
84
|
+
* closed and `aria-expanded="false"`; a click or tap on the field, typing or
|
|
85
|
+
* ArrowDown opens it. Use it in forms that move focus into the field
|
|
86
|
+
* themselves, on open or to the first invalid field after a rejected save.
|
|
87
|
+
* Escape and choosing an option work as before.
|
|
88
|
+
* @default true
|
|
89
|
+
*/
|
|
90
|
+
readonly openOnFocus: import("@angular/core").InputSignal<boolean>;
|
|
79
91
|
/**
|
|
80
92
|
* The `value` of the selected option, two-way (3.29, regeln row 2):
|
|
81
93
|
* `[(selectedValue)]="month"`. Shows the matching option's label (from
|
|
@@ -110,9 +122,24 @@ export declare class ComboboxComponent implements ControlValueAccessor {
|
|
|
110
122
|
/**
|
|
111
123
|
* `lang` of the open dropdown (3.30.2, WCAG 3.1.2): the overlay renders under
|
|
112
124
|
* `<body>`, so it takes the host's closest `[lang]`, else none. Read at every
|
|
113
|
-
* opening
|
|
125
|
+
* opening and, since 3.33, again when a `lang` changes while the list is
|
|
126
|
+
* open; `null` while closed, so the server never reads the DOM.
|
|
114
127
|
*/
|
|
115
128
|
protected readonly panelLang: import("@angular/core").Signal<string | null>;
|
|
129
|
+
/**
|
|
130
|
+
* The field's width in px while the list is open (3.33, request 052,
|
|
131
|
+
* 049-n2), read at every opening: the list's floor (`min-width`), so it is
|
|
132
|
+
* at least as wide as the field and wider content still grows it. `null`
|
|
133
|
+
* while closed.
|
|
134
|
+
*/
|
|
135
|
+
protected readonly fieldWidth: import("@angular/core").Signal<number | null>;
|
|
136
|
+
/**
|
|
137
|
+
* Below the field, else above it, start- then end-aligned (the CDK's
|
|
138
|
+
* defaults), 4 px from the field on either side (3.33, 047-n1: the gap was
|
|
139
|
+
* a margin inside the pane, so it was 0 above). The list is never pushed,
|
|
140
|
+
* so the offset can't move it off the field.
|
|
141
|
+
*/
|
|
142
|
+
protected readonly positions: ConnectedPosition[];
|
|
116
143
|
/** Selected values */
|
|
117
144
|
protected selectedSingle: import("@angular/core").WritableSignal<ComboboxOption | null>;
|
|
118
145
|
protected selectedMultiple: import("@angular/core").WritableSignal<ComboboxOption[]>;
|
|
@@ -162,10 +189,16 @@ export declare class ComboboxComponent implements ControlValueAccessor {
|
|
|
162
189
|
* and its buttons keeps focus where it is (no blur, so the list stays open).
|
|
163
190
|
*/
|
|
164
191
|
protected onFrameMousedown(event: MouseEvent): void;
|
|
165
|
-
/**
|
|
192
|
+
/**
|
|
193
|
+
* A click on the frame beside the input and its buttons focuses the input.
|
|
194
|
+
* With `openOnFocus` off (3.32, request 051), a click or tap on the input or
|
|
195
|
+
* the frame also opens the list, which focus alone no longer does.
|
|
196
|
+
*/
|
|
166
197
|
protected onFrameClick(event: MouseEvent): void;
|
|
167
198
|
protected onInputChange(event: Event): void;
|
|
168
199
|
protected onInputFocus(): void;
|
|
200
|
+
/** Opens the list when there is something to show: a long enough query, or options. */
|
|
201
|
+
private openIfAny;
|
|
169
202
|
protected onInputBlur(): void;
|
|
170
203
|
protected onKeydown(event: KeyboardEvent): void;
|
|
171
204
|
protected selectOption(option: ComboboxOption): void;
|
|
@@ -254,6 +287,11 @@ export declare class ComboboxComponent implements ControlValueAccessor {
|
|
|
254
287
|
"required": false;
|
|
255
288
|
"isSignal": true;
|
|
256
289
|
};
|
|
290
|
+
"openOnFocus": {
|
|
291
|
+
"alias": "openOnFocus";
|
|
292
|
+
"required": false;
|
|
293
|
+
"isSignal": true;
|
|
294
|
+
};
|
|
257
295
|
"selectedValue": {
|
|
258
296
|
"alias": "selectedValue";
|
|
259
297
|
"required": false;
|
|
@@ -191,10 +191,13 @@ export declare class DatepickerComponent implements ControlValueAccessor {
|
|
|
191
191
|
readonly isDisabled: import("@angular/core").Signal<boolean>;
|
|
192
192
|
/** Month granularity (3.29). */
|
|
193
193
|
protected readonly isMonthMode: import("@angular/core").Signal<boolean>;
|
|
194
|
+
/** The host's closest `[lang]` while the panel is open, kept live (3.33, 049-n3). */
|
|
195
|
+
private readonly hostLang;
|
|
194
196
|
/**
|
|
195
197
|
* `lang` of the open panel (3.30.2, WCAG 3.1.2): the overlay renders under
|
|
196
198
|
* `<body>`, so it takes the host's closest `[lang]`, else `locale`, else none.
|
|
197
|
-
* Read at every opening
|
|
199
|
+
* Read at every opening and, since 3.33, again when a `lang` changes while
|
|
200
|
+
* the panel is open; `null` while closed, so the server never reads the DOM.
|
|
198
201
|
*/
|
|
199
202
|
protected readonly panelLang: import("@angular/core").Signal<string | null>;
|
|
200
203
|
readonly formattedDate: import("@angular/core").Signal<string>;
|
|
@@ -25,6 +25,12 @@ export type HeaderVariant = 'default' | 'transparent';
|
|
|
25
25
|
* `userAvatarColor`, in a 48 px round button).
|
|
26
26
|
*/
|
|
27
27
|
export type HeaderUserMenuTrigger = 'icon' | 'avatar';
|
|
28
|
+
/**
|
|
29
|
+
* Where the context button sits up to 640 px (3.33): `inline` in the end
|
|
30
|
+
* group, hidden up to 640 px (as before), or `below`, a row of its own under
|
|
31
|
+
* the title. From 641 px both render it inline.
|
|
32
|
+
*/
|
|
33
|
+
export type HeaderContextPlacement = 'inline' | 'below';
|
|
28
34
|
/**
|
|
29
35
|
* AppHeaderComponent - Global application header for the LifeCorner shell
|
|
30
36
|
*
|
|
@@ -67,6 +73,9 @@ export type HeaderUserMenuTrigger = 'icon' | 'avatar';
|
|
|
67
73
|
* as typed (no uppercase).
|
|
68
74
|
* - The account menu is at most `min(18rem, 100vw − 2rem)` wide; the name and
|
|
69
75
|
* email in its header wrap instead of being cut.
|
|
76
|
+
* - `contextPlacement="below"` (3.33): up to 640 px the context button
|
|
77
|
+
* (`contextName`) takes a full-width row of its own under the title instead
|
|
78
|
+
* of being hidden; from 641 px it sits in the end group as before.
|
|
70
79
|
* - Every built-in label is an input (`homeAriaLabel`, `hamburgerAriaLabel`,
|
|
71
80
|
* `userMenuAriaLabel`, `navAriaLabel`, `profileLabel`, `logoutLabel`,
|
|
72
81
|
* `themeToDarkAriaLabel`, `themeToLightAriaLabel`, `contextAriaLabel`);
|
|
@@ -201,6 +210,16 @@ export declare class HeaderComponent {
|
|
|
201
210
|
readonly autoToggleTheme: import("@angular/core").InputSignal<boolean>;
|
|
202
211
|
readonly contextName: import("@angular/core").InputSignal<string>;
|
|
203
212
|
readonly contextLabel: import("@angular/core").InputSignal<string>;
|
|
213
|
+
/**
|
|
214
|
+
* Where the context button sits up to 640 px (3.33, app-shell row 1).
|
|
215
|
+
* `'inline'`: in the end group, hidden up to 640 px (as before). `'below'`:
|
|
216
|
+
* up to 640 px a row of its own under the title, as wide as the header and
|
|
217
|
+
* at least 44 px high; title, `end` slot and account menu stay in the first
|
|
218
|
+
* row. From 641 px both render the button inline. No second row without
|
|
219
|
+
* `contextName`.
|
|
220
|
+
* @default 'inline'
|
|
221
|
+
*/
|
|
222
|
+
readonly contextPlacement: import("@angular/core").InputSignal<HeaderContextPlacement>;
|
|
204
223
|
readonly menuSize: import("@angular/core").InputSignal<"sm" | "md" | "lg">;
|
|
205
224
|
readonly showProfileMenuItem: import("@angular/core").InputSignal<boolean>;
|
|
206
225
|
/** Render the account menu (avatar trigger with Profile/Logout). */
|
|
@@ -281,6 +300,8 @@ export declare class HeaderComponent {
|
|
|
281
300
|
* `contextName`, so a voice-control user can say what they see.
|
|
282
301
|
*/
|
|
283
302
|
protected readonly contextButtonAriaLabel: import("@angular/core").Signal<string>;
|
|
303
|
+
/** `contextPlacement="below"` with a context button to place (3.33). */
|
|
304
|
+
protected readonly contextBelow: import("@angular/core").Signal<boolean>;
|
|
284
305
|
/**
|
|
285
306
|
* Get menu items for profile dropdown
|
|
286
307
|
*/
|
|
@@ -459,6 +480,11 @@ export declare class HeaderComponent {
|
|
|
459
480
|
"required": false;
|
|
460
481
|
"isSignal": true;
|
|
461
482
|
};
|
|
483
|
+
"contextPlacement": {
|
|
484
|
+
"alias": "contextPlacement";
|
|
485
|
+
"required": false;
|
|
486
|
+
"isSignal": true;
|
|
487
|
+
};
|
|
462
488
|
"menuSize": {
|
|
463
489
|
"alias": "menuSize";
|
|
464
490
|
"required": false;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Signal } from "@angular/core";
|
|
1
2
|
/**
|
|
2
3
|
* Accessible names that contain the visible text (WCAG 2.5.3 Label in Name,
|
|
3
4
|
* AS-33). The containment test follows axe-core 4.14's
|
|
@@ -35,4 +36,14 @@ export declare function nameWithText(label: string, text: string): string;
|
|
|
35
36
|
* The value is taken as is (`lang=""` is the unknown language, as DOM
|
|
36
37
|
* inheritance gives it). `null` without a DOM `closest` (server DOM).
|
|
37
38
|
*/
|
|
38
|
-
export declare function closestLang(el: Element | null | undefined): string | null;
|
|
39
|
+
export declare function closestLang(el: Element | null | undefined): string | null;
|
|
40
|
+
/**
|
|
41
|
+
* 3.33 (request 052, 049-n3): `closestLang` of the component's host while
|
|
42
|
+
* `open()` is true, `null` while it is false (so the server never reads the
|
|
43
|
+
* DOM). While open, one `MutationObserver` watches every `lang` attribute of
|
|
44
|
+
* the document, so a host language that changes with the panel open shows
|
|
45
|
+
* without reopening; it is disconnected when the panel closes or the
|
|
46
|
+
* component is destroyed. Call it in the component's injection context (it
|
|
47
|
+
* injects the host element and creates an effect).
|
|
48
|
+
*/
|
|
49
|
+
export declare function openPanelLang(open: Signal<boolean>): Signal<string | null>;
|
|
@@ -77,7 +77,12 @@ export interface ListItem {
|
|
|
77
77
|
id?: string;
|
|
78
78
|
/** Whether the item is disabled */
|
|
79
79
|
disabled?: boolean;
|
|
80
|
-
/**
|
|
80
|
+
/**
|
|
81
|
+
* Whether the item is the current choice: a tinted row and, since 3.33,
|
|
82
|
+
* `aria-current="true"` on the row element (the button with `clickable`,
|
|
83
|
+
* the link with `href`, the row otherwise). Pair it with a visible mark such
|
|
84
|
+
* as a badge; the tint alone is colour.
|
|
85
|
+
*/
|
|
81
86
|
selected?: boolean;
|
|
82
87
|
/** Any additional data */
|
|
83
88
|
[key: string]: any;
|
|
@@ -55,6 +55,9 @@ export type NextStepCardActionSize = 'sm' | 'md' | 'lg';
|
|
|
55
55
|
* `stacked` for several equal-width actions; `titleTag` for a heading
|
|
56
56
|
* - `actionsStyle="equal"` (3.16, BA-5): a question with two answers of the
|
|
57
57
|
* same weight, both outline buttons in the primary tone, primary first
|
|
58
|
+
* - `focusTitle()` (3.33): moves focus to the title (`tabindex="-1"`, out of
|
|
59
|
+
* the Tab order) after an action replaced the card's state, so focus
|
|
60
|
+
* doesn't fall back to the page (WCAG 2.4.3)
|
|
58
61
|
*
|
|
59
62
|
* The host is a size container (`container-type: inline-size`) and takes the
|
|
60
63
|
* full inline size of its parent.
|
|
@@ -168,6 +171,18 @@ export declare class NextStepCardComponent implements AfterViewChecked {
|
|
|
168
171
|
/** Emits when the secondary action is clicked. */
|
|
169
172
|
secondaryClick: import("@angular/core").OutputEmitterRef<void>;
|
|
170
173
|
private actionsSlot;
|
|
174
|
+
/** The title element of whichever `titleTag` branch is rendered. */
|
|
175
|
+
private titleEl;
|
|
176
|
+
/**
|
|
177
|
+
* Moves focus to the card's title (3.33), e.g. after an action replaced the
|
|
178
|
+
* card's state, so focus doesn't fall back to the page (WCAG 2.4.3). Gives
|
|
179
|
+
* the title `tabindex="-1"` first: it stays out of the Tab order. Call it
|
|
180
|
+
* after the new state has rendered, e.g.
|
|
181
|
+
* `afterNextRender(() => this.card().focusTitle(), { injector })` with
|
|
182
|
+
* `card = viewChild.required(NextStepCardComponent)`. Does nothing while no
|
|
183
|
+
* title is rendered.
|
|
184
|
+
*/
|
|
185
|
+
focusTitle(): void;
|
|
171
186
|
/**
|
|
172
187
|
* Whether content was projected into `[slot='actions']` — projected actions
|
|
173
188
|
* fully replace the built-in buttons.
|
|
@@ -124,6 +124,13 @@ export declare class SelectComponent implements ControlValueAccessor {
|
|
|
124
124
|
* Read at every opening; `null` while closed, so the server never reads the DOM.
|
|
125
125
|
*/
|
|
126
126
|
protected readonly panelLang: import("@angular/core").Signal<string | null>;
|
|
127
|
+
/**
|
|
128
|
+
* The trigger's width in px while the dropdown is open (3.33, request 052,
|
|
129
|
+
* 050-n4), read at every opening: the panel's floor next to its 200 px
|
|
130
|
+
* (`min-width`), so it is at least as wide as the field and wider content
|
|
131
|
+
* still grows it. `null` while closed.
|
|
132
|
+
*/
|
|
133
|
+
protected readonly fieldWidth: import("@angular/core").Signal<number | null>;
|
|
127
134
|
readonly filteredOptions: import("@angular/core").Signal<SelectOption[]>;
|
|
128
135
|
/** Keyboard highlight; resets whenever the visible option list changes so it never points at a stale row. */
|
|
129
136
|
readonly highlightedIndex: import("@angular/core").WritableSignal<number>;
|
|
@@ -209,7 +209,9 @@ export declare class SidenavComponent implements OnDestroy {
|
|
|
209
209
|
* the collapsed rail, where labels are hidden, the label and this text name
|
|
210
210
|
* the item. `{value}` is replaced with the badge value, e.g.
|
|
211
211
|
* `'{value} offen'` → "Stammdaten 1 offen". The badge itself is hidden from
|
|
212
|
-
* screen readers. Empty: the badge isn't read.
|
|
212
|
+
* screen readers. Empty: the badge isn't read. Since 3.33 a badged rail
|
|
213
|
+
* item has no `title` (it repeated the label as a description); its
|
|
214
|
+
* tooltip still shows on hover and focus.
|
|
213
215
|
*
|
|
214
216
|
* @default '{value}'
|
|
215
217
|
*/
|
|
@@ -192,9 +192,13 @@ type ToastLive = 'polite' | 'assertive';
|
|
|
192
192
|
* hidden, the bottom offset is the safe-area inset. Without a shell, and in
|
|
193
193
|
* prerendered HTML, both are `0px`, as before. Every stack is at most as high
|
|
194
194
|
* as the space between the offsets and scrolls when its toasts need more
|
|
195
|
-
* (200 % text), so each toast stays reachable.
|
|
196
|
-
*
|
|
197
|
-
*
|
|
195
|
+
* (200 % text), so each toast stays reachable.
|
|
196
|
+
*
|
|
197
|
+
* Bottom stacks show the newest toast nearest the edge (3.33); top stacks the
|
|
198
|
+
* oldest at the top. A `top-*` stack lies over the page on purpose (a toast
|
|
199
|
+
* stays visible wherever the page is scrolled), so in the shell it can cover
|
|
200
|
+
* a title right under the header (at 360 px a toast at y 72 over an h1 at
|
|
201
|
+
* y 80): prefer `bottom-*` there.
|
|
198
202
|
*
|
|
199
203
|
* CSS hooks (set them on `lc-toast-outlet` or any ancestor, e.g. `:root`;
|
|
200
204
|
* each wins over the shell's value; write lengths, `0px` rather than `0`):
|
|
@@ -215,6 +219,13 @@ export declare class ToastOutletComponent {
|
|
|
215
219
|
readonly closeAriaLabel: import("@angular/core").InputSignal<string>;
|
|
216
220
|
/** Toasts keyed by `${position}:${politeness}` in the order they were shown. */
|
|
217
221
|
private readonly groups;
|
|
222
|
+
/**
|
|
223
|
+
* 3.33 (request 052, 045-n2): per `bottom-*` stack, the live level of its
|
|
224
|
+
* newest toast. That region gets `lc-toast-outlet__region--latest` and is
|
|
225
|
+
* laid out at the edge, so the newest toast sits nearest the edge (and, in
|
|
226
|
+
* a scrolling stack, in view at rest). DOM and announcement order stay.
|
|
227
|
+
*/
|
|
228
|
+
protected readonly latestLive: import("@angular/core").Signal<Map<ToastPosition, ToastLive>>;
|
|
218
229
|
protected toastsFor(position: ToastPosition, live: ToastLive): readonly Toast[];
|
|
219
230
|
protected onClosed(id: string): void;
|
|
220
231
|
/** Errors and warnings interrupt (assertive), everything else waits its turn. */
|
|
@@ -17,6 +17,11 @@ export declare class TooltipContentComponent {
|
|
|
17
17
|
*
|
|
18
18
|
* The tooltip takes the `lang` of the host's closest `[lang]` (3.31).
|
|
19
19
|
*
|
|
20
|
+
* Shows on hover and on keyboard focus of the host or of an element inside it
|
|
21
|
+
* (3.32). On a wrapper such as `lc-button`, the tooltip describes and anchors
|
|
22
|
+
* to the wrapped `<button>`. On a host with several controls, the one that
|
|
23
|
+
* has focus is described (3.33); on hover, the first.
|
|
24
|
+
*
|
|
20
25
|
* @example
|
|
21
26
|
* ```html
|
|
22
27
|
* <button lcTooltip="Click me!" tooltipPosition="top">
|
|
@@ -50,10 +55,17 @@ export declare class TooltipDirective implements OnDestroy {
|
|
|
50
55
|
* @default false
|
|
51
56
|
*/
|
|
52
57
|
tooltipDisabled: import("@angular/core").InputSignal<boolean>;
|
|
53
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Id of the tooltip element; while shown, the `aria-describedby` of the
|
|
60
|
+
* described element (the host, or the focusable element it wraps, such as
|
|
61
|
+
* `lc-button`'s `<button>`) points at it.
|
|
62
|
+
*/
|
|
54
63
|
readonly tooltipId: string;
|
|
55
64
|
private overlayRef?;
|
|
56
65
|
private tooltipRef?;
|
|
66
|
+
private positionStrategy?;
|
|
67
|
+
/** The element whose `aria-describedby` holds the tooltip id while shown. */
|
|
68
|
+
private describedEl?;
|
|
57
69
|
private positionSubscription?;
|
|
58
70
|
private showTimeout?;
|
|
59
71
|
private hideTimeout?;
|
|
@@ -68,6 +80,14 @@ export declare class TooltipDirective implements OnDestroy {
|
|
|
68
80
|
onMouseLeave(): void;
|
|
69
81
|
onFocus(): void;
|
|
70
82
|
onBlur(): void;
|
|
83
|
+
/**
|
|
84
|
+
* 3.32 (request 051, 050-f3): `focus` does not bubble, so focus on an
|
|
85
|
+
* element inside the host (`lc-button`'s `<button>`) arrives as `focusin`.
|
|
86
|
+
* The host's own focus is handled by `onFocus`, so it is skipped here.
|
|
87
|
+
*/
|
|
88
|
+
onFocusIn(event: FocusEvent): void;
|
|
89
|
+
/** Hides when focus leaves the host from an element inside it (not when it moves within). */
|
|
90
|
+
onFocusOut(event: FocusEvent): void;
|
|
71
91
|
/**
|
|
72
92
|
* Show tooltip programmatically
|
|
73
93
|
*/
|
|
@@ -86,8 +106,17 @@ export declare class TooltipDirective implements OnDestroy {
|
|
|
86
106
|
/** Preferred position first, then the fallbacks the overlay may flip to. */
|
|
87
107
|
private getPositions;
|
|
88
108
|
private setPositionClass;
|
|
89
|
-
/**
|
|
109
|
+
/**
|
|
110
|
+
* The described element: the host if it is a control, else the control
|
|
111
|
+
* inside it that has focus (3.33, request 052, 051-n1: a host with several
|
|
112
|
+
* controls), else the first control inside it, else the host.
|
|
113
|
+
*/
|
|
114
|
+
private resolveControl;
|
|
115
|
+
/** The overlay origin: the control when it is the host's only element child (a wrapper), else the host. */
|
|
116
|
+
private resolveAnchor;
|
|
117
|
+
/** Append the tooltip id to the element's aria-describedby without clobbering ids the caller set. */
|
|
90
118
|
private addDescribedBy;
|
|
119
|
+
/** Removes the tooltip id from the element it was added to, keeping the other ids. */
|
|
91
120
|
private removeDescribedBy;
|
|
92
121
|
private clearTimeouts;
|
|
93
122
|
private cleanup;
|