@lifecorner/angular-ui-kit 3.26.0 → 3.27.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-callout.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-callout.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-chat.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-chat.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-color-picker.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-color-picker.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-date-range-picker.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-date-range-picker.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-datepicker.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-datepicker.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-dependency-viewer.mjs +47 -9
- package/fesm2022/lifecorner-angular-ui-kit-dependency-viewer.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-description-list.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-description-list.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-document-viewer.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-document-viewer.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs +4 -4
- package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-filter-bar.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-filter-bar.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-gallery.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-gallery.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-gantt-chart.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-gantt-chart.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-header.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-header.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-input.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-input.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-kanban-board.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-kanban-board.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-notification-center.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-number-input.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-number-input.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-rich-text-editor.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-rich-text-editor.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-scatter-plot.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-scatter-plot.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-search-input.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-search-input.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-select.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-select.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-split-pane.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-split-pane.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-tabs.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-tabs.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-toggle-group.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-toggle-group.mjs.map +1 -1
- package/package.json +1 -1
- package/src/styles/_reset.scss +4 -2
- package/src/styles/focus-indicators.spec.ts +199 -0
- package/types/lifecorner-angular-ui-kit-dependency-viewer.d.ts +33 -4
- package/types/lifecorner-angular-ui-kit-file-upload.d.ts +6 -1
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { compile } from 'sass';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Request 042 (3.27, design 042 D-1 to D-9): the guard for F-18 and F-19.
|
|
6
|
+
* Every element of design §6.1 rows B–F has a `:focus-visible` indicator of its
|
|
7
|
+
* own (kit ring: 2 px `--color-primary-500`), `Highlight` in forced colours;
|
|
8
|
+
* the global button reset only takes the ring from mouse focus; a field's hover
|
|
9
|
+
* rule never applies while it is focused. Read from the compiled SCSS sources.
|
|
10
|
+
*/
|
|
11
|
+
const sheets = new Map<string, string>();
|
|
12
|
+
const css = (file: string): string => {
|
|
13
|
+
if (!sheets.has(file)) sheets.set(file, compile(resolve(__dirname, file.startsWith('_') ? file : `../lib/${file}`)).css);
|
|
14
|
+
return sheets.get(file) as string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const rulesOf = (text: string) =>
|
|
18
|
+
[...text.matchAll(/([^{}]+)\{([^{}]*)\}/g)].map((m) => ({
|
|
19
|
+
selectors: m[1].split(/,(?![^(]*\))/).map((s) => s.trim()),
|
|
20
|
+
body: m[2],
|
|
21
|
+
}));
|
|
22
|
+
const decls = (body: string): Record<string, string> =>
|
|
23
|
+
Object.fromEntries([...body.matchAll(/([\w-]+):\s*([^;]+);/g)].map((m) => [m[1], m[2].replace(/\s+/g, ' ').trim()]));
|
|
24
|
+
const forcedBlock = /@media \(forced-colors: active\) \{((?:[^{}]*\{[^{}]*\})*)\s*\}/g;
|
|
25
|
+
|
|
26
|
+
/** Every rule naming `selector`, merged in source order, forced-colours blocks left out. */
|
|
27
|
+
const fills = (file: string, selector: string): Record<string, string> => {
|
|
28
|
+
const found = rulesOf(css(file).replace(forcedBlock, '')).filter((r) => r.selectors.includes(selector));
|
|
29
|
+
expect([file, selector, found.length > 0]).toEqual([file, selector, true]);
|
|
30
|
+
return Object.assign({}, ...found.map((r) => decls(r.body)));
|
|
31
|
+
};
|
|
32
|
+
/** The same, inside the forced-colours blocks only. */
|
|
33
|
+
const forcedFills = (file: string, selector: string): Record<string, string> => {
|
|
34
|
+
const inner = [...css(file).matchAll(forcedBlock)].map((m) => m[1]).join('\n');
|
|
35
|
+
return Object.assign({}, ...rulesOf(inner).filter((r) => r.selectors.includes(selector)).map((r) => decls(r.body)));
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const RING = '2px solid var(--color-primary-500)';
|
|
39
|
+
|
|
40
|
+
/** Design 042 §3: [sheet, element class, outline offset]. Rows B–D, kanban (D-3), remove (D-8), dep canvas (D-7). */
|
|
41
|
+
const rings: [string, string, string][] = [
|
|
42
|
+
['rich-text-editor/rich-text-editor.component.scss', '.lc-rich-text-editor__toolbar-btn', '2px'],
|
|
43
|
+
['rich-text-editor/rich-text-editor.component.scss', '.lc-rich-text-editor__mode-btn', '2px'],
|
|
44
|
+
['document-viewer/document-viewer.component.scss', '.doc-viewer__btn', '2px'],
|
|
45
|
+
['document-viewer/document-viewer.component.scss', '.doc-viewer__download-btn', '2px'],
|
|
46
|
+
['filter-bar/filter-bar.component.scss', '.lc-filter-bar__toggle-btn', '-2px'],
|
|
47
|
+
['select/select.component.scss', '.lc-select__clear', '2px'],
|
|
48
|
+
['gallery/gallery.component.scss', '.gallery__filter-btn', '2px'],
|
|
49
|
+
['log-viewer/log-viewer.component.scss', '.lc-log-viewer__level-pill', '2px'],
|
|
50
|
+
['callout/callout.component.scss', '.callout__dismiss', '2px'],
|
|
51
|
+
['notification-center/notification-center.component.scss', '.lc-nc__action-btn', '2px'],
|
|
52
|
+
['notification-center/notification-center.component.scss', '.lc-nc__filter', '2px'],
|
|
53
|
+
['tabs/tabs.component.scss', '.lc-tabs__panel', '-2px'],
|
|
54
|
+
['chat/chat.component.scss', '.lc-chat__attachment', '2px'],
|
|
55
|
+
['chat/chat.component.scss', '.lc-chat__attach-btn', '2px'],
|
|
56
|
+
['chat/chat.component.scss', '.lc-chat__send-btn', '2px'],
|
|
57
|
+
['chat/chat.component.scss', '.lc-chat__pending-remove', '2px'],
|
|
58
|
+
['description-list/description-list.component.scss', '.lc-dl__link', '2px'],
|
|
59
|
+
['color-picker/color-picker.component.scss', '.color-picker__native', '2px'],
|
|
60
|
+
['table/table.component.scss', '.lc-table__checkbox', '2px'],
|
|
61
|
+
['number-input/number-input.component.scss', '.number-input__button', '-2px'],
|
|
62
|
+
['tag-input/tag-input.component.scss', '.lc-tag-input__tag-remove', '2px'],
|
|
63
|
+
['file-upload/file-upload.component.scss', '.file-upload__file-remove', '2px'],
|
|
64
|
+
// Review 042 r1: inset, an outside ring measured 2.19:1 on an over-limit column (light).
|
|
65
|
+
['kanban-board/kanban-board.component.scss', '.lc-kanban__card', '-2px'],
|
|
66
|
+
['dependency-viewer/dependency-viewer.component.scss', '.dep-viewer__canvas--scroll', '-2px'],
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
describe('Focus indicators (request 042)', () => {
|
|
70
|
+
describe('D-1: the global button reset', () => {
|
|
71
|
+
it('AC-5: takes the outline from mouse focus only, so a button with no ring of its own keeps the browser ring on Tab', () => {
|
|
72
|
+
const reset = css('_reset.scss');
|
|
73
|
+
expect(reset).toMatch(/\nbutton:focus:not\(:focus-visible\) \{\s*outline: none;\s*\}/);
|
|
74
|
+
expect(reset).not.toMatch(/\nbutton:focus \{/);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('D-2: the kit ring on every §6.1 B–D element', () => {
|
|
79
|
+
it.each(rings)('AC-2: %s %s draws a solid 2 px primary-500 outline (offset %s), no halo', (file, cls, offset) => {
|
|
80
|
+
const ring = fills(file, `${cls}:focus-visible`);
|
|
81
|
+
expect([ring.outline, ring['outline-offset']]).toEqual([RING, offset]);
|
|
82
|
+
expect(ring['box-shadow']).toBeUndefined();
|
|
83
|
+
expect(ring.transition).toBeUndefined();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it.each(rings)('AC-3: %s %s turns Highlight in forced colours', (file, cls) => {
|
|
87
|
+
expect(forcedFills(file, `${cls}:focus-visible`)['outline-color']).toBe('Highlight');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it.each(rings)('AC-4: %s %s draws no ring on mouse focus (no outline on a bare :focus rule)', (file, cls) => {
|
|
91
|
+
const bare = rulesOf(css(file).replace(forcedBlock, '')).filter((r) => r.selectors.includes(`${cls}:focus`));
|
|
92
|
+
for (const r of bare) expect([cls, decls(r.body).outline ?? 'none']).toEqual([cls, 'none']);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('AC-2: the dimmed buttons turn fully opaque on focus (the ring is not dimmed with them)', () => {
|
|
96
|
+
expect(fills('callout/callout.component.scss', '.callout__dismiss').opacity).toBe('0.6');
|
|
97
|
+
expect(fills('callout/callout.component.scss', '.callout__dismiss:focus-visible').opacity).toBe('1');
|
|
98
|
+
expect(fills('tag-input/tag-input.component.scss', '.lc-tag-input__tag-remove').opacity).toBe('0.7');
|
|
99
|
+
expect(fills('tag-input/tag-input.component.scss', '.lc-tag-input__tag-remove:focus-visible').opacity).toBe('1');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Offset 2px → -2px by review 042 r1: outside, the ring sat on the column tint
|
|
103
|
+
// (2.19:1 on an over-limit column, light); inset it sits on the card (4.36 / 9.12).
|
|
104
|
+
it('AC-2: the kanban card drops its 20 % halo (1.28:1) for an inset ring on the card and keeps the border change', () => {
|
|
105
|
+
expect(fills('kanban-board/kanban-board.component.scss', '.lc-kanban__card:focus-visible')).toEqual({
|
|
106
|
+
outline: RING,
|
|
107
|
+
'outline-offset': '-2px',
|
|
108
|
+
'border-color': 'var(--color-primary-500)',
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('D-3: indicators that forced colours drop', () => {
|
|
114
|
+
const forced: [string, string, Record<string, string>][] = [
|
|
115
|
+
['toggle-group/toggle-group.component.scss', '.lc-toggle-group__btn:focus-visible', { outline: '2px solid Highlight', 'outline-offset': '-2px' }],
|
|
116
|
+
['list/list.component.scss', '.lc-list__item:focus-visible', { outline: '2px solid Highlight', 'outline-offset': '-2px' }],
|
|
117
|
+
['gantt-chart/gantt-chart.component.scss', '.lc-gantt__bar:focus-visible', { outline: '2px solid Highlight', 'outline-offset': '2px' }],
|
|
118
|
+
['split-pane/split-pane.component.scss', '.lc-split-pane__separator:focus-visible', { outline: '2px solid Highlight', 'outline-offset': '-2px' }],
|
|
119
|
+
['scatter-plot/scatter-plot.component.scss', '.lc-scatter-plot__dot:focus-visible', { stroke: 'Highlight' }],
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
it.each(forced)('AC-3: %s %s has its own forced-colours indicator', (file, selector, expected) => {
|
|
123
|
+
expect(forcedFills(file, selector)).toEqual(expected);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('AC-7: their normal-mode indicators are unchanged', () => {
|
|
127
|
+
expect(fills('toggle-group/toggle-group.component.scss', '.lc-toggle-group__btn:focus-visible')['box-shadow']).toBe('inset 0 0 0 2px var(--color-primary-500)');
|
|
128
|
+
expect(fills('list/list.component.scss', '.lc-list__item:focus-visible')['box-shadow']).toBe('inset 0 0 0 2px var(--color-primary-500)');
|
|
129
|
+
expect(fills('gantt-chart/gantt-chart.component.scss', '.lc-gantt__bar:focus-visible')['box-shadow']).toBe('0 0 0 2px var(--color-surface), 0 0 0 4px var(--color-primary-500)');
|
|
130
|
+
expect(fills('split-pane/split-pane.component.scss', '.lc-split-pane__separator:focus-visible::before').background).toBe('var(--color-primary-500)');
|
|
131
|
+
expect(fills('scatter-plot/scatter-plot.component.scss', '.lc-scatter-plot__dot:focus-visible').stroke).toBe('var(--color-text-primary)');
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('D-9: the rich-text editing surface', () => {
|
|
136
|
+
const frame = '.lc-rich-text-editor:has(.lc-rich-text-editor__rich:focus-visible, .lc-rich-text-editor__textarea:focus-visible)';
|
|
137
|
+
const file = 'rich-text-editor/rich-text-editor.component.scss';
|
|
138
|
+
|
|
139
|
+
it('AC-2: the frame shows the field ring (lc-input pattern); the surfaces keep outline: none', () => {
|
|
140
|
+
expect(fills(file, frame)).toEqual({ 'border-color': 'var(--color-primary-500)', outline: RING, 'outline-offset': '0px' });
|
|
141
|
+
expect(fills(file, '.lc-rich-text-editor__rich').outline).toBe('none');
|
|
142
|
+
expect(fills(file, '.lc-rich-text-editor__textarea').outline).toBe('none');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('AC-3: the ancestor ring has its own Highlight rule', () => {
|
|
146
|
+
expect(forcedFills(file, frame)).toEqual({ 'border-color': 'Highlight', 'outline-color': 'Highlight' });
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe('D-4: the light header on a dark page', () => {
|
|
151
|
+
const file = 'header/header.component.scss';
|
|
152
|
+
const own = ['.lc-header__nav-link', '.lc-header__logo', '.lc-header__context-info', '.lc-header__profile-trigger'];
|
|
153
|
+
|
|
154
|
+
it('AC-2: the header-owned rings read the private ring colour, primary-500 by default', () => {
|
|
155
|
+
for (const cls of own) {
|
|
156
|
+
expect([cls, fills(file, `${cls}:focus-visible`).outline]).toEqual([cls, '2px solid var(--_lc-header-ring, var(--color-primary-500))']);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('AC-2: theme="light" pins the light primary-500: 4.36:1 on its white bar in both themes (dark primary-500 was 1.89)', () => {
|
|
161
|
+
const light = fills(file, '.lc-header--light');
|
|
162
|
+
expect([light['--_lc-header-ring'], light['--lc-header-bg']]).toEqual(['#208497', '#ffffff']);
|
|
163
|
+
const lum = (hex: string) =>
|
|
164
|
+
[1, 3, 5]
|
|
165
|
+
.map((i) => parseInt(hex.slice(i, i + 2), 16) / 255)
|
|
166
|
+
.map((v) => (v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4))
|
|
167
|
+
.reduce((sum, v, i) => sum + v * [0.2126, 0.7152, 0.0722][i], 0);
|
|
168
|
+
expect(Math.floor(((lum('#ffffff') + 0.05) / (lum('#208497') + 0.05)) * 100) / 100).toBe(4.36);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe('D-6: focus wins over hover in every field with the pattern (F-19)', () => {
|
|
173
|
+
// [sheet, hover selector after the fix, declarations it keeps]
|
|
174
|
+
const hovers: [string, string, Record<string, string>][] = [
|
|
175
|
+
['textarea/textarea.component.scss', '.lc-textarea--outline:hover:not(:disabled):not(:read-only):not(:focus)', { 'border-color': 'var(--color-primary-400)' }],
|
|
176
|
+
['textarea/textarea.component.scss', '.lc-textarea--filled:hover:not(:disabled):not(:read-only):not(:focus)', { 'background-color': 'var(--color-surface-hover)' }],
|
|
177
|
+
['datepicker/datepicker.component.scss', '.lc-datepicker:hover:not(:disabled):not([readonly]):not(:focus)', { 'border-color': 'var(--color-primary-400)' }],
|
|
178
|
+
['datepicker/datepicker.component.scss', '.lc-datepicker.lc-datepicker--filled:hover:not(:disabled):not([readonly]):not(:focus)', { 'background-color': 'var(--color-surface-hover)', 'border-color': 'var(--color-border)' }],
|
|
179
|
+
['input/input.component.scss', '.input-container:hover:not(.input-disabled):not(.input-readonly):not(:focus-within)', { 'border-color': 'var(--color-text-secondary)' }],
|
|
180
|
+
['select/select.component.scss', '.lc-select:hover:not(.lc-select--disabled):not(:focus)', { 'border-color': 'var(--color-text-secondary)' }],
|
|
181
|
+
['select/select.component.scss', '.lc-select.lc-select--filled:hover:not(.lc-select--disabled):not(:focus)', { 'background-color': 'var(--color-surface-hover)', 'border-color': 'var(--color-border)' }],
|
|
182
|
+
['search-input/search-input.component.scss', '.search-input:hover:not(.search-input--disabled):not(:focus-within)', { 'border-color': 'var(--color-text-secondary)' }],
|
|
183
|
+
['tag-input/tag-input.component.scss', '.lc-tag-input__container:hover:not(.lc-tag-input__container--focused)', { 'border-color': 'var(--color-text-secondary)' }],
|
|
184
|
+
['date-range-picker/date-range-picker.component.scss', '.lc-drp__field:hover:not(.lc-drp__field--disabled):not(:focus-within)', { 'border-color': 'var(--color-text-secondary)' }],
|
|
185
|
+
];
|
|
186
|
+
|
|
187
|
+
it.each(hovers)('AC-6/AC-7: %s %s keeps its look and excludes focus', (file, selector, expected) => {
|
|
188
|
+
expect(fills(file, selector)).toEqual(expected);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it.each(hovers)('AC-6: %s has no other hover rule on that field', (file, selector) => {
|
|
192
|
+
const field = selector.slice(0, selector.indexOf(':hover'));
|
|
193
|
+
const others = rulesOf(css(file).replace(forcedBlock, ''))
|
|
194
|
+
.flatMap((r) => r.selectors)
|
|
195
|
+
.filter((s) => s.startsWith(`${field}:hover`) && s !== selector);
|
|
196
|
+
expect(others).toEqual([]);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
});
|
|
@@ -269,19 +269,30 @@ export declare class DependencyViewerComponent {
|
|
|
269
269
|
* `contain` fits both axes, so everything is visible at once but a big graph
|
|
270
270
|
* shrinks to get there. `fit-width` fits the width only and lets the viewer grow
|
|
271
271
|
* as tall as the graph needs, so the nodes keep a readable size and the *page*
|
|
272
|
-
* scrolls;
|
|
273
|
-
* process view generally wants `fit-width`.
|
|
272
|
+
* scrolls; past `minNodeSize`, a graph wider than the frame scrolls sideways
|
|
273
|
+
* inside it. Past roughly 20 nodes a process view generally wants `fit-width`.
|
|
274
274
|
*/
|
|
275
275
|
readonly fitMode: import("@angular/core").InputSignal<DependencyFitMode | null>;
|
|
276
276
|
/**
|
|
277
277
|
* Smallest node width, in px, that fitting may shrink to. Below it the fit stops
|
|
278
278
|
* scaling down and lets the graph overflow instead — which `fit-width` turns into
|
|
279
|
-
*
|
|
279
|
+
* sideways scrolling of the frame (pinned to the start, see `regionLabel`), and
|
|
280
|
+
* `contain` into clipping. Pair it with `fit-width`.
|
|
280
281
|
*
|
|
281
282
|
* Nodes are 160px wide unscaled (wider — up to 240px — when the graph's labels
|
|
282
|
-
* need it), so e.g. `80` allows shrinking to half size.
|
|
283
|
+
* need it), so e.g. `80` allows shrinking to half size. `200` keeps the 12 px
|
|
284
|
+
* labels at 10 px or more whatever the node width (160–240 px): the phone
|
|
285
|
+
* recipe is `fitMode="fit-width"` with `minNodeSize` 200.
|
|
283
286
|
*/
|
|
284
287
|
readonly minNodeSize: import("@angular/core").InputSignal<number | null>;
|
|
288
|
+
/**
|
|
289
|
+
* Accessible name of the canvas while it scrolls sideways (`fitMode="fit-width"`
|
|
290
|
+
* with a `minNodeSize` the frame can't hold). It is then a focusable
|
|
291
|
+
* `role="region"`, scrolled with the arrow keys. Set a short, localised name
|
|
292
|
+
* that is unique on the page. Not used otherwise.
|
|
293
|
+
* @default 'Dependency graph'
|
|
294
|
+
*/
|
|
295
|
+
readonly regionLabel: import("@angular/core").InputSignal<string>;
|
|
285
296
|
/**
|
|
286
297
|
* Viewport navigation: drag-to-pan, wheel-zoom and the toolbar's zoom buttons.
|
|
287
298
|
* `false` freezes the viewport — node selection, expansion and collapse keep
|
|
@@ -378,6 +389,19 @@ export declare class DependencyViewerComponent {
|
|
|
378
389
|
protected effectiveFitMode: import("@angular/core").Signal<DependencyFitMode>;
|
|
379
390
|
/** The host only gives up its configured height when the fit made it grow. */
|
|
380
391
|
protected hostHeight: import("@angular/core").Signal<string>;
|
|
392
|
+
/**
|
|
393
|
+
* Horizontal extent of the graph, in layout units, while `fit-width` is held
|
|
394
|
+
* at `minNodeSize` and the graph is wider than the frame; `null` otherwise.
|
|
395
|
+
* The canvas then scrolls sideways instead of clipping both ends.
|
|
396
|
+
*/
|
|
397
|
+
private scrollBox;
|
|
398
|
+
/** Whether the canvas scrolls sideways (see `scrollBox`). */
|
|
399
|
+
protected scrolling: import("@angular/core").Signal<boolean>;
|
|
400
|
+
/**
|
|
401
|
+
* Scroll width of the canvas: the graph's right edge at the current pan and
|
|
402
|
+
* zoom. The svg's own box is unscaled, so it can't be the scroll extent.
|
|
403
|
+
*/
|
|
404
|
+
protected scrollWidth: import("@angular/core").Signal<string | null>;
|
|
381
405
|
/** `minNodeSize` expressed as the scale it forbids fitting to go below. */
|
|
382
406
|
private minFitScale;
|
|
383
407
|
constructor();
|
|
@@ -500,6 +524,11 @@ export declare class DependencyViewerComponent {
|
|
|
500
524
|
"required": false;
|
|
501
525
|
"isSignal": true;
|
|
502
526
|
};
|
|
527
|
+
"regionLabel": {
|
|
528
|
+
"alias": "regionLabel";
|
|
529
|
+
"required": false;
|
|
530
|
+
"isSignal": true;
|
|
531
|
+
};
|
|
503
532
|
"interactive": {
|
|
504
533
|
"alias": "interactive";
|
|
505
534
|
"required": false;
|
|
@@ -78,7 +78,12 @@ export declare class FileUploadComponent {
|
|
|
78
78
|
* @default 'Max:'
|
|
79
79
|
*/
|
|
80
80
|
maxSizeLabel: import("@angular/core").InputSignal<string>;
|
|
81
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* Accessible name of a selected file's remove button, followed by the file
|
|
83
|
+
* name ("Remove file bericht.csv"), so each button in the list has its own
|
|
84
|
+
* name.
|
|
85
|
+
* @default 'Remove file'
|
|
86
|
+
*/
|
|
82
87
|
removeFileAriaLabel: import("@angular/core").InputSignal<string>;
|
|
83
88
|
/**
|
|
84
89
|
* BCP 47 locale of the shown sizes (max line, drop zone name, file list):
|