@lifecorner/angular-ui-kit 3.16.0 → 3.18.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 +17 -1
- package/fesm2022/lifecorner-angular-ui-kit-bottom-nav.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-button.mjs +12 -7
- package/fesm2022/lifecorner-angular-ui-kit-button.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-modal.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-modal.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-progress-bar.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-progress-bar.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-table.mjs +6 -4
- package/fesm2022/lifecorner-angular-ui-kit-table.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-toast.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-toast.mjs.map +1 -1
- package/package.json +1 -1
- package/src/styles/theme-contrast.spec.ts +57 -0
- package/types/lifecorner-angular-ui-kit-bottom-nav.d.ts +42 -8
- package/types/lifecorner-angular-ui-kit-button.d.ts +28 -1
- package/types/lifecorner-angular-ui-kit-progress-bar.d.ts +12 -1
- package/types/lifecorner-angular-ui-kit-table.d.ts +19 -1
- package/types/lifecorner-angular-ui-kit-toast.d.ts +3 -1
|
@@ -302,3 +302,60 @@ describe('Component pairings of 3.16 (request 022)', () => {
|
|
|
302
302
|
});
|
|
303
303
|
});
|
|
304
304
|
});
|
|
305
|
+
|
|
306
|
+
// Request 021 (3.17): the lc-list status badges (12 px / 500, so 4.5:1) and the
|
|
307
|
+
// focus ring of a loading lc-button, which its 0.5 opacity fades. The token
|
|
308
|
+
// names come from the compiled component stylesheets, the values from the themes.
|
|
309
|
+
describe('Component pairings of 3.17 (request 021)', () => {
|
|
310
|
+
const listCss = compile(resolve(__dirname, '../lib/list/list.component.scss')).css;
|
|
311
|
+
const buttonCss = compile(resolve(__dirname, '../lib/button/button.component.scss')).css;
|
|
312
|
+
|
|
313
|
+
/** The background and ink token of a list badge variant (a fallback after the name is ignored). */
|
|
314
|
+
function badgeTokens(variant: string): [string, string] {
|
|
315
|
+
const body = listCss.match(new RegExp(`\\n\\.lc-list__badge--${variant} \\{([^}]*)\\}`))?.[1] ?? '';
|
|
316
|
+
const bg = body.match(/background-color: var\((--[\w-]+)/)?.[1] ?? '';
|
|
317
|
+
const ink = body.match(/\scolor: var\((--[\w-]+)/)?.[1] ?? '';
|
|
318
|
+
expect([bg, ink]).toEqual([expect.stringMatching(/^--color-/), expect.stringMatching(/^--color-/)]);
|
|
319
|
+
return [bg, ink];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** Design 021 §5: the minimum over the bases, per palette [light, dark]. */
|
|
323
|
+
const badges: [string, [string, string], [number, number]][] = [
|
|
324
|
+
['success', ['--color-success-subtle', '--color-on-success-subtle'], [4.85, 5.17]],
|
|
325
|
+
['warning', ['--color-warning-subtle', '--color-on-warning-subtle'], [5.52, 4.61]],
|
|
326
|
+
['error', ['--color-error-100', '--color-on-error-subtle'], [6.33, 7.17]],
|
|
327
|
+
['primary', ['--color-primary-100', '--color-primary-700'], [7.85, 9.68]],
|
|
328
|
+
['default', ['--color-surface-hover', '--color-text-secondary'], [6.87, 8.18]],
|
|
329
|
+
];
|
|
330
|
+
|
|
331
|
+
describe.each(palettes.map(([name, tokens], i) => [name, tokens, i] as const))('%s palette', (_palette, tokens, i) => {
|
|
332
|
+
/** The grounds a list sits on: the page, the card surfaces and a hovered row (over the surface). */
|
|
333
|
+
const bases: Rgba[] = [
|
|
334
|
+
color(tokens, '--color-background'),
|
|
335
|
+
color(tokens, '--color-surface'),
|
|
336
|
+
over(color(tokens, '--color-surface-2'), color(tokens, '--color-surface')),
|
|
337
|
+
over(color(tokens, '--color-surface-hover'), color(tokens, '--color-surface')),
|
|
338
|
+
].map((c) => over(c, color(tokens, '--color-background')));
|
|
339
|
+
|
|
340
|
+
it.each(badges)('AC-12: the %s list badge reaches 4.5:1 on every ground', (variant, pair, values) => {
|
|
341
|
+
expect(badgeTokens(variant)).toEqual(pair);
|
|
342
|
+
const [bg, ink] = pair;
|
|
343
|
+
const min = Math.min(
|
|
344
|
+
...bases.map((base) => {
|
|
345
|
+
const fill = over(color(tokens, bg), base);
|
|
346
|
+
return ratio(over(color(tokens, ink), fill), fill);
|
|
347
|
+
}),
|
|
348
|
+
);
|
|
349
|
+
expect(min).toBeCloseTo(values[i], 2);
|
|
350
|
+
expect(min).toBeGreaterThanOrEqual(4.5);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('AC-7: the ring of a focused loading button (--color-text-primary at 50 %) reaches 3:1 on the page and the surfaces', () => {
|
|
354
|
+
expect(buttonCss).toMatch(/\n\.btn\.btn-loading:focus-visible \{\s*outline-color: var\(--color-text-primary\);/);
|
|
355
|
+
const ink = color(tokens, '--color-text-primary');
|
|
356
|
+
for (const base of bases.slice(0, 3)) {
|
|
357
|
+
expect(ratio(over([ink[0], ink[1], ink[2], 0.5], base), base)).toBeGreaterThanOrEqual(3);
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
});
|
|
@@ -47,6 +47,8 @@ interface BottomNavEntry {
|
|
|
47
47
|
* - More than `maxItems` destinations: the first `maxItems − 1` stay, the
|
|
48
48
|
* rest move behind a "More" entry (`moreLabel`, `moreIcon`, `moreRoute`);
|
|
49
49
|
* without `moreRoute` the entry is a button that emits `moreClick`.
|
|
50
|
+
* - `showMore`: "More" on request, also when every destination fits (fixed
|
|
51
|
+
* slots); it takes one of the `maxItems` entries.
|
|
50
52
|
* - Tapping the current item emits `reselect` (scroll the page to the top).
|
|
51
53
|
* - `variant="floating"`: an inset pill 16 px from the sides and 16 px above
|
|
52
54
|
* the safe-area inset, radius 28 px, elevated. `docked` (default) spans the
|
|
@@ -89,17 +91,41 @@ export declare class BottomNavComponent {
|
|
|
89
91
|
*/
|
|
90
92
|
readonly fixed: import("@angular/core").InputSignal<boolean>;
|
|
91
93
|
/**
|
|
92
|
-
* Most entries the bar shows. With more destinations
|
|
93
|
-
* `maxItems − 1` stay and the rest go behind the
|
|
94
|
+
* Most entries the bar shows, "More" included. With more destinations, or
|
|
95
|
+
* with `showMore`, the first `maxItems − 1` stay and the rest go behind the
|
|
96
|
+
* "More" entry.
|
|
97
|
+
*
|
|
98
|
+
* @default 5
|
|
94
99
|
*/
|
|
95
100
|
readonly maxItems: import("@angular/core").InputSignal<number>;
|
|
96
|
-
/**
|
|
101
|
+
/**
|
|
102
|
+
* Always end the bar with the "More" entry (`moreLabel`, `moreIcon`, and
|
|
103
|
+
* `moreRoute` or `moreClick`), even when every destination fits: for a bar
|
|
104
|
+
* with fixed slots, such as two destinations plus "More". "More" takes one
|
|
105
|
+
* of the `maxItems` entries, so the first `maxItems − 1` destinations stay
|
|
106
|
+
* and any others move behind it, as on overflow.
|
|
107
|
+
*
|
|
108
|
+
* @default false
|
|
109
|
+
*/
|
|
110
|
+
readonly showMore: import("@angular/core").InputSignal<boolean>;
|
|
111
|
+
/**
|
|
112
|
+
* Label of the "More" entry (on overflow or with `showMore`).
|
|
113
|
+
*
|
|
114
|
+
* @default 'More'
|
|
115
|
+
*/
|
|
97
116
|
readonly moreLabel: import("@angular/core").InputSignal<string>;
|
|
98
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* Icon of the "More" entry (on overflow or with `showMore`).
|
|
119
|
+
*
|
|
120
|
+
* @default 'dots'
|
|
121
|
+
*/
|
|
99
122
|
readonly moreIcon: import("@angular/core").InputSignal<string>;
|
|
100
123
|
/**
|
|
101
|
-
* Route of the
|
|
102
|
-
* destinations
|
|
124
|
+
* Route of the "More" entry (on overflow or with `showMore`): a page that
|
|
125
|
+
* lists the remaining destinations. Empty renders the entry as a button that
|
|
126
|
+
* emits `moreClick`.
|
|
127
|
+
*
|
|
128
|
+
* @default ''
|
|
103
129
|
*/
|
|
104
130
|
readonly moreRoute: import("@angular/core").InputSignal<string>;
|
|
105
131
|
/**
|
|
@@ -109,13 +135,16 @@ export declare class BottomNavComponent {
|
|
|
109
135
|
readonly badgeAriaLabel: import("@angular/core").InputSignal<string>;
|
|
110
136
|
/** The current item was tapped again (e.g. scroll the page to the top). */
|
|
111
137
|
readonly reselect: import("@angular/core").OutputEmitterRef<BottomNavItem>;
|
|
112
|
-
/** The
|
|
138
|
+
/** The "More" entry was tapped while it has no `moreRoute`. */
|
|
113
139
|
readonly moreClick: import("@angular/core").OutputEmitterRef<void>;
|
|
114
140
|
/** Destinations in display order. */
|
|
115
141
|
private readonly sortedItems;
|
|
116
142
|
/** Path of `activeRoute` without query and fragment. */
|
|
117
143
|
private readonly activePath;
|
|
118
|
-
/**
|
|
144
|
+
/**
|
|
145
|
+
* Entries as rendered: destinations plus, on overflow or with `showMore`,
|
|
146
|
+
* the "More" entry, which then takes one of the `maxItems` entries.
|
|
147
|
+
*/
|
|
119
148
|
protected readonly entries: import("@angular/core").Signal<BottomNavEntry[]>;
|
|
120
149
|
protected readonly navClasses: import("@angular/core").Signal<string>;
|
|
121
150
|
/** Visually hidden badge text for screen readers. */
|
|
@@ -156,6 +185,11 @@ export declare class BottomNavComponent {
|
|
|
156
185
|
"required": false;
|
|
157
186
|
"isSignal": true;
|
|
158
187
|
};
|
|
188
|
+
"showMore": {
|
|
189
|
+
"alias": "showMore";
|
|
190
|
+
"required": false;
|
|
191
|
+
"isSignal": true;
|
|
192
|
+
};
|
|
159
193
|
"moreLabel": {
|
|
160
194
|
"alias": "moreLabel";
|
|
161
195
|
"required": false;
|
|
@@ -28,7 +28,28 @@ export declare class ButtonComponent {
|
|
|
28
28
|
*/
|
|
29
29
|
readonly tone: import("@angular/core").InputSignal<ButtonTone>;
|
|
30
30
|
readonly size: import("@angular/core").InputSignal<ButtonSize>;
|
|
31
|
+
/**
|
|
32
|
+
* Natively disabled: the button leaves the focus order and ignores clicks.
|
|
33
|
+
* For a temporary busy state that keeps focus, use `loading`.
|
|
34
|
+
* @default false
|
|
35
|
+
*/
|
|
31
36
|
readonly disabled: import("@angular/core").InputSignal<boolean>;
|
|
37
|
+
/**
|
|
38
|
+
* Shows a spinner in place of the icon slots and makes the button
|
|
39
|
+
* unavailable without removing it from the focus order (3.17). The inner
|
|
40
|
+
* `<button>` gets `aria-disabled="true"` and `aria-busy="true"` instead of
|
|
41
|
+
* the native `disabled`, so a focused button keeps focus. While it is
|
|
42
|
+
* loading, click, Enter and Space are ignored: `clicked` is not emitted, the
|
|
43
|
+
* click event stops at the button (a `(click)` on the host doesn't fire),
|
|
44
|
+
* and a `type="submit"`/`"reset"` button doesn't submit or reset its form.
|
|
45
|
+
* The look is unchanged: spinner, 50 % opacity, no hover. Screen readers may
|
|
46
|
+
* announce the state change ("unavailable"/"dimmed"), but the button doesn't
|
|
47
|
+
* announce the outcome: the page does that, for example with a polite live
|
|
48
|
+
* region ("USM wird geladen.", then the result). `disabledReason` also
|
|
49
|
+
* applies while loading. If `disabled` is set too, the native `disabled`
|
|
50
|
+
* wins.
|
|
51
|
+
* @default false
|
|
52
|
+
*/
|
|
32
53
|
readonly loading: import("@angular/core").InputSignal<boolean>;
|
|
33
54
|
readonly iconOnly: import("@angular/core").InputSignal<boolean>;
|
|
34
55
|
readonly fullWidth: import("@angular/core").InputSignal<boolean>;
|
|
@@ -72,7 +93,13 @@ export declare class ButtonComponent {
|
|
|
72
93
|
* `title` otherwise.
|
|
73
94
|
*/
|
|
74
95
|
readonly effectiveTitle: import("@angular/core").Signal<string | null>;
|
|
75
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Emits `clicked` unless the button is disabled or loading. While loading
|
|
98
|
+
* (and not natively disabled) the event is cancelled and stopped, so a form
|
|
99
|
+
* isn't submitted and a host `(click)` doesn't fire. The parameter is
|
|
100
|
+
* optional, so callers without an argument still work.
|
|
101
|
+
*/
|
|
102
|
+
handleClick(event?: Event): void;
|
|
76
103
|
handleFocus(): void;
|
|
77
104
|
handleBlur(): void;
|
|
78
105
|
focus(): void;
|
|
@@ -12,6 +12,13 @@ export type ProgressBarVariant = 'linear' | 'circular';
|
|
|
12
12
|
* - Animated value transitions
|
|
13
13
|
* - Accessible with ARIA progressbar role
|
|
14
14
|
*
|
|
15
|
+
* The bar is a visual aid; the text next to it carries the meaning. Write the
|
|
16
|
+
* status as text ('USM wird geladen …') and announce changes from a live
|
|
17
|
+
* region (`role="status"`). Screen readers don't announce a progress bar's
|
|
18
|
+
* changes by themselves. Give `ariaLabel` the same words as the status. Under
|
|
19
|
+
* `forced-colors: active` the fill is `Highlight`, the linear track gets a
|
|
20
|
+
* 1 px `CanvasText` outline and the circle's track is `GrayText` (3.17).
|
|
21
|
+
*
|
|
15
22
|
* @example
|
|
16
23
|
* ```html
|
|
17
24
|
* <lc-progress-bar [value]="75" color="primary" size="md"></lc-progress-bar>
|
|
@@ -57,7 +64,11 @@ export declare class ProgressBarComponent {
|
|
|
57
64
|
*/
|
|
58
65
|
inline: import("@angular/core").InputSignal<boolean>;
|
|
59
66
|
/**
|
|
60
|
-
*
|
|
67
|
+
* Progress of unknown length: no value (`aria-valuenow` is absent) and no
|
|
68
|
+
* label. The linear bar moves a 40 % segment across the track, and the
|
|
69
|
+
* circle spins. Under `prefers-reduced-motion: reduce` nothing moves: the
|
|
70
|
+
* linear bar shows a full-width striped fill and the circle a dashed ring,
|
|
71
|
+
* so it still reads as busy, not as a value (3.17).
|
|
61
72
|
* @default false
|
|
62
73
|
*/
|
|
63
74
|
indeterminate: import("@angular/core").InputSignal<boolean>;
|
|
@@ -37,7 +37,11 @@ export interface TableColumn {
|
|
|
37
37
|
width?: string;
|
|
38
38
|
/** Optional CSS class(es) applied to both th and td cells */
|
|
39
39
|
cssClass?: string;
|
|
40
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* Optional CSS class(es) for td cells only (string or resolver callback).
|
|
42
|
+
* A class colour or font size loses to the kit's cell rule; set them with
|
|
43
|
+
* `cellStyle`.
|
|
44
|
+
*/
|
|
41
45
|
cellClass?: TableCellClass;
|
|
42
46
|
/** Optional inline styles for td cells only (object or resolver callback) */
|
|
43
47
|
cellStyle?: TableCellStyle;
|
|
@@ -49,7 +53,21 @@ export interface TableColumn {
|
|
|
49
53
|
editType?: 'text' | 'number' | 'select';
|
|
50
54
|
/** Options for select edit type */
|
|
51
55
|
editOptions?: string[];
|
|
56
|
+
/**
|
|
57
|
+
* Horizontal alignment of the column's header and cells. `end` aligns both
|
|
58
|
+
* at the end edge (right in LTR) and sets `font-variant-numeric:
|
|
59
|
+
* tabular-nums`, for amounts, counts and percentages. A sort arrow then sits
|
|
60
|
+
* before the label, so the label stays flush with the figures. Adds
|
|
61
|
+
* `lc-table__cell--end` to the `th` and every `td`. It wins over a
|
|
62
|
+
* `cellClass` that sets `text-align`; use `cellStyle` to override it. A
|
|
63
|
+
* custom `lcTableCell` template inherits the alignment, but a flex or block
|
|
64
|
+
* child needs its own `justify-content: flex-end`. (3.17)
|
|
65
|
+
* @default 'start'
|
|
66
|
+
*/
|
|
67
|
+
align?: TableColumnAlign;
|
|
52
68
|
}
|
|
69
|
+
/** Horizontal alignment of a table column (3.17). */
|
|
70
|
+
export type TableColumnAlign = 'start' | 'end';
|
|
53
71
|
/**
|
|
54
72
|
* A row-level action rendered in the table's actions column (e.g. Freigeben, Ablehnen).
|
|
55
73
|
* Action button clicks never trigger the row's `rowClick` output.
|
|
@@ -22,7 +22,9 @@ export interface Toast extends Required<Omit<ToastConfig, 'action'>> {
|
|
|
22
22
|
* Toast service for showing ephemeral notifications.
|
|
23
23
|
*
|
|
24
24
|
* The toasts are rendered by `<lc-toast-outlet />`, which the application
|
|
25
|
-
* places once (e.g. in its shell); without it nothing is shown.
|
|
25
|
+
* places once (e.g. in its shell); without it nothing is shown. The options,
|
|
26
|
+
* their defaults, the timing and how a toast is announced to screen readers
|
|
27
|
+
* are documented under *Feedback/Toast Outlet* (3.17).
|
|
26
28
|
*
|
|
27
29
|
* @example
|
|
28
30
|
* ```typescript
|