@lifecorner/angular-ui-kit 3.24.1 → 3.25.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-code-block.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-code-block.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-dependency-viewer.mjs +7 -7
- package/fesm2022/lifecorner-angular-ui-kit-dependency-viewer.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-description-list.mjs +12 -4
- package/fesm2022/lifecorner-angular-ui-kit-description-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-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-password-input.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-password-input.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-select.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-select.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-slider.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-slider.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-tree-view.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-tree-view.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-verification-code-input.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-verification-code-input.mjs.map +1 -1
- package/package.json +1 -1
- package/src/styles/theme-contrast.spec.ts +235 -7
- package/types/lifecorner-angular-ui-kit-description-list.d.ts +17 -5
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { compile } from 'sass';
|
|
2
|
-
import { readFileSync } from 'fs';
|
|
2
|
+
import { readFileSync, readdirSync } from 'fs';
|
|
3
3
|
import { resolve } from 'path';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -743,10 +743,13 @@ describe('Component pairings of 3.21.1 (request 029)', () => {
|
|
|
743
743
|
);
|
|
744
744
|
});
|
|
745
745
|
|
|
746
|
-
it('AC-10: the progress track is unchanged; primary
|
|
746
|
+
it('AC-10: the progress track is unchanged; primary reads the theme token (request 039), secondary the data mark (036)', () => {
|
|
747
747
|
// The base rule (the forced-colours block repeats the selector with an outline).
|
|
748
748
|
expect(sheets.progress).toMatch(/\n\.progress-bar__track \{\s*flex: 1;\s*background-color: var\(--color-surface-hover\);/);
|
|
749
|
-
|
|
749
|
+
// 039 (F-14): the raw #208497 was 2.89:1 against the dark track; primary
|
|
750
|
+
// now reads --color-primary-500 (same hex in light, 6.69:1 dark). This
|
|
751
|
+
// line asserted the raw hex before 3.25.
|
|
752
|
+
expect(fills('progress', '.progress-bar--primary .progress-bar__fill')['background-color']).toBe('var(--color-primary-500)');
|
|
750
753
|
// 036 (F-11): the raw secondary-500 was 2.83:1 against the light track;
|
|
751
754
|
// secondary now reads the data-mark token (4.19 light, 4.05 dark).
|
|
752
755
|
expect(fills('progress', '.progress-bar--secondary .progress-bar__fill')['background-color']).toBe('var(--color-secondary-graphic)');
|
|
@@ -1325,7 +1328,8 @@ describe('Component pairings of 3.23.1 (request 036)', () => {
|
|
|
1325
1328
|
['dl', '.lc-dl__value--primary', { color: 'var(--color-on-primary-subtle)', 'font-weight': 'var(--font-weight-semibold)' }],
|
|
1326
1329
|
['dl', '.lc-dl__link', { color: 'var(--color-link)', 'text-decoration': 'none' }],
|
|
1327
1330
|
['dl', '.lc-dl__link:hover', { color: 'var(--color-link-hover)', 'text-decoration': 'underline' }],
|
|
1328
|
-
|
|
1331
|
+
// 039 (F-17): the ring moved from the row to the row's button (same box).
|
|
1332
|
+
['dl', '.lc-dl__action:focus-visible', { outline: '2px solid var(--color-primary-500)', 'outline-offset': '2px' }],
|
|
1329
1333
|
// F-8: unread message and time in the secondary ink.
|
|
1330
1334
|
['nc', '.lc-nc__item--unread .lc-nc__item-message', { color: secondary }],
|
|
1331
1335
|
['nc', '.lc-nc__item--unread .lc-nc__item-time', { color: secondary }],
|
|
@@ -1347,10 +1351,11 @@ describe('Component pairings of 3.23.1 (request 036)', () => {
|
|
|
1347
1351
|
});
|
|
1348
1352
|
|
|
1349
1353
|
it('AC-2: the clickable row has no halo any more; forced colours draw the outline in Highlight', () => {
|
|
1350
|
-
|
|
1354
|
+
// 039 (F-17): the focus rules moved to the row's button `.lc-dl__action`.
|
|
1355
|
+
const clickable = rulesOf(sheets.dl).filter((r) => r.selectors.some((s) => /lc-dl__row--clickable|lc-dl__action/.test(s)));
|
|
1351
1356
|
for (const r of clickable) expect(decls(r.body)['box-shadow']).toBeUndefined();
|
|
1352
1357
|
expect(sheets.dl).not.toMatch(/color-mix/);
|
|
1353
|
-
expect(forced('dl')).toMatch(/\.lc-
|
|
1358
|
+
expect(forced('dl')).toMatch(/\.lc-dl__action:focus-visible \{\s*outline-color: Highlight;\s*\}/);
|
|
1354
1359
|
});
|
|
1355
1360
|
|
|
1356
1361
|
it('AC-9: forced colours paint every search match, the terminal one too, with Mark / MarkText', () => {
|
|
@@ -1416,7 +1421,8 @@ describe('Component pairings of 3.23.1 (request 036)', () => {
|
|
|
1416
1421
|
|
|
1417
1422
|
function measure() {
|
|
1418
1423
|
const dlInk = (selector: string) => value(fills('dl', selector).color);
|
|
1419
|
-
|
|
1424
|
+
// 039 (F-17): the ring is the row button's (it covers the row's padding box).
|
|
1425
|
+
const ringInk = value(fills('dl', '.lc-dl__action:focus-visible').outline.replace(/^2px solid /, ''));
|
|
1420
1426
|
const dl = {
|
|
1421
1427
|
linkPage: lowest(dlInk('.lc-dl__link'), pages),
|
|
1422
1428
|
linkHover: lowest(dlInk('.lc-dl__link'), hovered),
|
|
@@ -1550,3 +1556,225 @@ describe('Component pairings of 3.23.1 (request 036)', () => {
|
|
|
1550
1556
|
expect(Math.min(...Object.values(terminal))).toBeGreaterThanOrEqual(4.5);
|
|
1551
1557
|
});
|
|
1552
1558
|
});
|
|
1559
|
+
|
|
1560
|
+
/**
|
|
1561
|
+
* Request 039 (3.25.0): F-14 the progress-bar primary fill, F-15 focus rings
|
|
1562
|
+
* instead of 20–40 % halos, F-16 the dependency-viewer node and toggle
|
|
1563
|
+
* borders. Design 039 §3 (rings) and §5 (ratios), floored, [light, dark].
|
|
1564
|
+
*/
|
|
1565
|
+
describe('Component pairings of 3.25.0 (request 039)', () => {
|
|
1566
|
+
const lib = (path: string) => resolve(__dirname, '../lib', path);
|
|
1567
|
+
const sheets = {
|
|
1568
|
+
progress: compile(lib('progress-bar/progress-bar.component.scss')).css,
|
|
1569
|
+
ring: compile(lib('progress-ring/progress-ring.component.scss')).css,
|
|
1570
|
+
nc: compile(lib('notification-center/notification-center.component.scss')).css,
|
|
1571
|
+
log: compile(lib('log-viewer/log-viewer.component.scss')).css,
|
|
1572
|
+
code: compile(lib('code-block/code-block.component.scss')).css,
|
|
1573
|
+
tree: compile(lib('tree-view/tree-view.component.scss')).css,
|
|
1574
|
+
slider: compile(lib('slider/slider.component.scss')).css,
|
|
1575
|
+
select: compile(lib('select/select.component.scss')).css,
|
|
1576
|
+
digits: compile(lib('verification-code-input/verification-code-input.component.scss')).css,
|
|
1577
|
+
password: compile(lib('password-input/password-input.component.scss')).css,
|
|
1578
|
+
textarea: compile(lib('textarea/textarea.component.scss')).css,
|
|
1579
|
+
number: compile(lib('number-input/number-input.component.scss')).css,
|
|
1580
|
+
combobox: compile(lib('combobox/combobox.component.scss')).css,
|
|
1581
|
+
dep: compile(lib('dependency-viewer/dependency-viewer.component.scss')).css,
|
|
1582
|
+
dl: compile(lib('description-list/description-list.component.scss')).css,
|
|
1583
|
+
};
|
|
1584
|
+
type Sheet = keyof typeof sheets;
|
|
1585
|
+
const depTs = readFileSync(lib('dependency-viewer/dependency-viewer.component.ts'), 'utf8');
|
|
1586
|
+
const depHtml = readFileSync(lib('dependency-viewer/dependency-viewer.component.html'), 'utf8');
|
|
1587
|
+
|
|
1588
|
+
const rulesOf = (css: string) =>
|
|
1589
|
+
[...css.matchAll(/([^{}]+)\{([^{}]*)\}/g)].map((m) => ({
|
|
1590
|
+
selectors: m[1].split(/,(?![^(]*\))/).map((s) => s.trim()),
|
|
1591
|
+
body: m[2],
|
|
1592
|
+
}));
|
|
1593
|
+
const decls = (body: string): Record<string, string> =>
|
|
1594
|
+
Object.fromEntries([...body.matchAll(/([\w-]+):\s*([^;]+);/g)].map((m) => [m[1], m[2].replace(/\s+/g, ' ').trim()]));
|
|
1595
|
+
const forcedBlock = /@media \(forced-colors: active\) \{((?:[^{}]*\{[^{}]*\})*)\s*\}/g;
|
|
1596
|
+
/** Every rule naming `selector`, merged in source order, forced-colours blocks left out. */
|
|
1597
|
+
const fills = (sheet: Sheet, selector: string): Record<string, string> => {
|
|
1598
|
+
const found = rulesOf(sheets[sheet].replace(forcedBlock, '')).filter((r) => r.selectors.includes(selector));
|
|
1599
|
+
expect([sheet, selector, found.length > 0]).toEqual([sheet, selector, true]);
|
|
1600
|
+
return Object.assign({}, ...found.map((r) => decls(r.body)));
|
|
1601
|
+
};
|
|
1602
|
+
/** The same, inside the forced-colours blocks only. */
|
|
1603
|
+
const forcedFills = (sheet: Sheet, selector: string): Record<string, string> => {
|
|
1604
|
+
const inner = [...sheets[sheet].matchAll(forcedBlock)].map((m) => m[1]).join('\n');
|
|
1605
|
+
return Object.assign({}, ...rulesOf(inner).filter((r) => r.selectors.includes(selector)).map((r) => decls(r.body)));
|
|
1606
|
+
};
|
|
1607
|
+
const primary = '2px solid var(--color-primary-500)';
|
|
1608
|
+
const halo = /color-mix\(in srgb, var\(--color-(primary-500|error-600|error-default)\) \d+%|rgba\(32, 132, 151, 0?\.\d+\)/;
|
|
1609
|
+
|
|
1610
|
+
// Design 039 §3: [sheet, selector, outline, offset]. Every row was a halo or
|
|
1611
|
+
// had no focus style (D-4: NC action, log tool buttons, dependency toolbar).
|
|
1612
|
+
const rings: [Sheet, string, string, string][] = [
|
|
1613
|
+
['nc', '.lc-nc__item-title:focus-visible', primary, '2px'],
|
|
1614
|
+
['nc', '.lc-nc__item-action:focus-visible', primary, '2px'],
|
|
1615
|
+
['nc', '.lc-nc__item-dismiss:focus-visible', primary, '2px'],
|
|
1616
|
+
['log', '.lc-log-viewer__tool-btn:focus-visible', primary, '2px'],
|
|
1617
|
+
['log', '.lc-log-viewer__line--clickable:focus-visible', primary, '-2px'],
|
|
1618
|
+
['log', '.lc-log-viewer__jump-bottom:focus-visible', primary, '2px'],
|
|
1619
|
+
['code', '.code-block__copy:focus-visible', primary, '-2px'],
|
|
1620
|
+
['tree', '.lc-tree-view__tool-btn:focus-visible', primary, '2px'],
|
|
1621
|
+
['tree', '.lc-tree-view__item:focus-visible', primary, '-2px'],
|
|
1622
|
+
['select', '.lc-select__option.lc-select__option--highlighted', primary, '-2px'],
|
|
1623
|
+
['digits', '.lc-digit-input:focus', primary, '0'],
|
|
1624
|
+
['dep', '.dep-viewer__btn:focus-visible', primary, '2px'],
|
|
1625
|
+
['dl', '.lc-dl__action:focus-visible', primary, '2px'],
|
|
1626
|
+
['password', '.lc-input.lc-input-error:focus', '2px solid var(--color-error-600)', '0'],
|
|
1627
|
+
['textarea', '.lc-textarea--error:focus', '2px solid var(--color-error-600)', '0'],
|
|
1628
|
+
['number', '.number-input--error .number-input__control:focus-within', '2px solid var(--color-error-600)', '0'],
|
|
1629
|
+
['combobox', '.lc-combobox--error .lc-combobox__input-wrapper:focus-within', '2px solid var(--color-error-default)', '0'],
|
|
1630
|
+
];
|
|
1631
|
+
|
|
1632
|
+
it.each(rings)('AC-2: %s %s draws a solid 2 px outline, no halo', (sheet, selector, outline, offset) => {
|
|
1633
|
+
const found = fills(sheet, selector);
|
|
1634
|
+
expect([found.outline, found['outline-offset']]).toEqual([outline, offset]);
|
|
1635
|
+
expect(found['box-shadow'] ?? 'none').not.toMatch(halo);
|
|
1636
|
+
});
|
|
1637
|
+
|
|
1638
|
+
it.each(rings)('AC-3: %s %s turns Highlight in forced colours', (sheet, selector) => {
|
|
1639
|
+
expect(forcedFills(sheet, selector)['outline-color']).toBe('Highlight');
|
|
1640
|
+
});
|
|
1641
|
+
|
|
1642
|
+
it('AC-2, AC-3: an error digit keeps the error colour, and forced colours name its selector too', () => {
|
|
1643
|
+
expect(fills('digits', '.lc-digit-input.lc-digit-input-error:focus')).toEqual({ 'outline-color': 'var(--color-error-600)' });
|
|
1644
|
+
expect(forcedFills('digits', '.lc-digit-input.lc-digit-input-error:focus')['outline-color']).toBe('Highlight');
|
|
1645
|
+
});
|
|
1646
|
+
|
|
1647
|
+
it('AC-2: the log tool buttons show the ring at full opacity (0.7 gave 2.66:1)', () => {
|
|
1648
|
+
expect(fills('log', '.lc-log-viewer__tool-btn:focus-visible').opacity).toBe('1');
|
|
1649
|
+
});
|
|
1650
|
+
|
|
1651
|
+
it('AC-2, AC-3: the slider thumb ring is solid primary-500; forced colours outline the input', () => {
|
|
1652
|
+
for (const thumb of ['::-webkit-slider-thumb', '::-moz-range-thumb']) {
|
|
1653
|
+
expect(fills('slider', `.slider__input:focus-visible${thumb}`)['box-shadow']).toBe('0 1px 3px rgba(0, 0, 0, 0.15), 0 0 0 3px var(--color-primary-500)');
|
|
1654
|
+
}
|
|
1655
|
+
expect(forcedFills('slider', '.slider__input:focus-visible')).toEqual({ outline: '2px solid Highlight', 'outline-offset': '8px' });
|
|
1656
|
+
});
|
|
1657
|
+
|
|
1658
|
+
it('AC-11: the rings add no transition', () => {
|
|
1659
|
+
for (const [sheet, selector] of rings) expect([selector, fills(sheet, selector).transition]).toEqual([selector, undefined]);
|
|
1660
|
+
});
|
|
1661
|
+
|
|
1662
|
+
/**
|
|
1663
|
+
* AC-5 guard (design 039 §9): no focus, `--focused` or `--highlighted` rule
|
|
1664
|
+
* in lib/**\/*.scss draws a ≤ 40 % primary or error halo unless the same rule
|
|
1665
|
+
* also changes the border or draws an outline (the "keep" rows of the
|
|
1666
|
+
* inventory: the field border turns primary-500, ≥ 4.17 / 7.57).
|
|
1667
|
+
*/
|
|
1668
|
+
it('AC-5: no focus style is a translucent halo alone', () => {
|
|
1669
|
+
const root = resolve(__dirname, '../lib');
|
|
1670
|
+
const files = (readdirSync(root, { recursive: true }) as string[]).filter((f) => /\.scss$/.test(f) && !/(^|\/)_/.test(f));
|
|
1671
|
+
expect(files.length).toBeGreaterThan(100);
|
|
1672
|
+
const offenders: string[] = [];
|
|
1673
|
+
let focusRules = 0;
|
|
1674
|
+
for (const file of files) {
|
|
1675
|
+
for (const r of rulesOf(compile(resolve(root, file)).css)) {
|
|
1676
|
+
if (!r.selectors.some((s) => /:focus|--focused|--highlighted/.test(s))) continue;
|
|
1677
|
+
focusRules++;
|
|
1678
|
+
const d = decls(r.body);
|
|
1679
|
+
const shadow = d['box-shadow'] ?? '';
|
|
1680
|
+
const weak = [...shadow.matchAll(/color-mix\(in srgb, var\(--color-(?:primary-500|error-600|error-default)\) (\d+)%/g)].some((m) => Number(m[1]) <= 60)
|
|
1681
|
+
|| [...shadow.matchAll(/rgba\(32, 132, 151, (0?\.\d+)\)/g)].some((m) => Number(m[1]) <= 0.4);
|
|
1682
|
+
const outline = (d.outline !== undefined && !/^(none|0)\b/.test(d.outline)) || d['outline-color'] !== undefined;
|
|
1683
|
+
if (weak && !d['border-color'] && !outline) offenders.push(`${file}: ${r.selectors.join(', ')}`);
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
expect(focusRules).toBeGreaterThan(100);
|
|
1687
|
+
expect(offenders).toEqual([]);
|
|
1688
|
+
});
|
|
1689
|
+
|
|
1690
|
+
it('AC-1: the progress primary reads the theme token in the bar and the circle, as the ring does', () => {
|
|
1691
|
+
expect(fills('progress', '.progress-bar--primary .progress-bar__fill')['background-color']).toBe('var(--color-primary-500)');
|
|
1692
|
+
expect(fills('progress', '.progress-bar-circular--primary .progress-bar-circular__fill').stroke).toBe('var(--color-primary-500)');
|
|
1693
|
+
expect(fills('ring', '.lc-progress-ring__value--primary').stroke).toBe('var(--color-primary-500)');
|
|
1694
|
+
expect(sheets.progress).not.toMatch(/#208497/i);
|
|
1695
|
+
});
|
|
1696
|
+
|
|
1697
|
+
it('AC-6: default and muted nodes and the toggle ring read the field border, active primary-600', () => {
|
|
1698
|
+
const border = (status: string) => depTs.match(new RegExp(`\\n ${status}:\\s*\\{ bg: '[^']+', border: '([^']+)'`))?.[1];
|
|
1699
|
+
expect(['default', 'active', 'muted'].map(border)).toEqual(['var(--color-field-border)', 'var(--color-primary-600)', 'var(--color-field-border)']);
|
|
1700
|
+
expect(depHtml.match(/<circle[^>]*fill="var\(--color-neutral-100\)" stroke="var\(--color-field-border\)" stroke-width="1"/g)).toHaveLength(2);
|
|
1701
|
+
expect(depHtml).not.toMatch(/--color-neutral-300|--color-neutral-200/);
|
|
1702
|
+
// The selected stroke and the edge, label and legend colours of 036 stay.
|
|
1703
|
+
expect(depHtml).toContain(`[attr.stroke]="selectedNodeId() === node.id ? 'var(--color-primary-500)' : getNodeBorder(node)"`);
|
|
1704
|
+
});
|
|
1705
|
+
|
|
1706
|
+
const expected = {
|
|
1707
|
+
progressTrack: [3.96, 6.69],
|
|
1708
|
+
ringPages: [4.17, 7.57],
|
|
1709
|
+
ringHovered: [3.96, 6.69],
|
|
1710
|
+
ringUnread: [3.75, 5.77],
|
|
1711
|
+
// Design §5 gave 3.23 light with the hover composited over the tints; the
|
|
1712
|
+
// tint replaces the hover fill, so the lowest real line is the error tint.
|
|
1713
|
+
ringLogLines: [3.49, 7.47],
|
|
1714
|
+
ringTerminalLines: [3.28, 7.58],
|
|
1715
|
+
ringLogToolbar: [4.02, 9.12],
|
|
1716
|
+
ringCodeHeader: [3.78, 8.74],
|
|
1717
|
+
ringTreeRows: [3.75, 6.96],
|
|
1718
|
+
errorPages: [8.02, 7.21],
|
|
1719
|
+
depField: [4.83, 6.7],
|
|
1720
|
+
depFieldOwnFill: [4.39, 5.69],
|
|
1721
|
+
depActive: [5.77, 10.8],
|
|
1722
|
+
};
|
|
1723
|
+
const floor = (n: number) => Math.floor(n * 100) / 100;
|
|
1724
|
+
|
|
1725
|
+
describe.each(palettes.map(([name, tokens], i) => [name, tokens, i] as const))('%s palette', (_palette, tokens, i) => {
|
|
1726
|
+
const c = (name: string) => color(tokens, name);
|
|
1727
|
+
const lowest = (fg: Rgba, bases: Rgba[]) => Math.min(...bases.map((bg) => ratio(over(fg, bg), bg)));
|
|
1728
|
+
|
|
1729
|
+
function measure() {
|
|
1730
|
+
const S = c('--color-surface');
|
|
1731
|
+
const pages = [c('--color-background'), S, c('--color-surface-2')];
|
|
1732
|
+
const hovered = pages.map((p) => over(c('--color-surface-hover'), p));
|
|
1733
|
+
const ring = c('--color-primary-500');
|
|
1734
|
+
|
|
1735
|
+
const log = (selector: string) => parse(fills('log', selector)['background-color']);
|
|
1736
|
+
const logBg = S;
|
|
1737
|
+
const T = log('.lc-log-viewer--terminal');
|
|
1738
|
+
const hover = log('.lc-log-viewer__line:hover');
|
|
1739
|
+
// A tint (0,2,0, later) replaces the hover fill on warn and error lines,
|
|
1740
|
+
// so hover only lightens the untinted lines.
|
|
1741
|
+
const lines = (base: Rgba, variant: string) => [
|
|
1742
|
+
base,
|
|
1743
|
+
over(hover, base),
|
|
1744
|
+
over(log(`${variant} .lc-log-viewer__line--warn`), base),
|
|
1745
|
+
over(log(`${variant} .lc-log-viewer__line--error`), base),
|
|
1746
|
+
];
|
|
1747
|
+
|
|
1748
|
+
const codeVar = (name: string) => parse(sheets.code.match(new RegExp(`${name}: (#[0-9a-f]{6})`, 'i'))?.[1] ?? '');
|
|
1749
|
+
const header = over(parse(fills('code', '.code-block__header')['background-color']), codeVar('--lc-code-block-bg'));
|
|
1750
|
+
const copyHover = over(parse(fills('code', '.code-block__copy:hover')['background-color']), header);
|
|
1751
|
+
|
|
1752
|
+
const treeRows = [S, over(c('--color-surface-hover'), S), over(c('--color-primary-50'), S), over(c('--color-primary-100'), S)];
|
|
1753
|
+
const field = c('--color-field-border');
|
|
1754
|
+
|
|
1755
|
+
return {
|
|
1756
|
+
progressTrack: lowest(ring, pages.map((p) => over(c('--color-surface-hover'), p))),
|
|
1757
|
+
ringPages: lowest(ring, pages),
|
|
1758
|
+
ringHovered: lowest(ring, hovered),
|
|
1759
|
+
ringUnread: lowest(ring, [S, c('--color-surface-2')].map((p) => over(c('--color-surface-selected'), p))),
|
|
1760
|
+
ringLogLines: lowest(ring, lines(logBg, '.lc-log-viewer--log')),
|
|
1761
|
+
ringTerminalLines: lowest(ring, lines(T, '.lc-log-viewer--terminal')),
|
|
1762
|
+
ringLogToolbar: lowest(ring, [S, parse('#181825')]),
|
|
1763
|
+
ringCodeHeader: lowest(ring, [header, copyHover]),
|
|
1764
|
+
ringTreeRows: lowest(ring, treeRows),
|
|
1765
|
+
errorPages: Math.min(lowest(c('--color-error-600'), pages), lowest(c('--color-error-default'), pages)),
|
|
1766
|
+
depField: lowest(field, [S]),
|
|
1767
|
+
depFieldOwnFill: lowest(field, [over(c('--color-neutral-50'), S), over(c('--color-neutral-100'), S)]),
|
|
1768
|
+
depActive: lowest(c('--color-primary-600'), [S, over(c('--color-primary-50'), S)]),
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
it('AC-1, AC-2, AC-6: the design 039 §5 ratios; fills and boundaries ≥ 3:1', () => {
|
|
1773
|
+
const m = measure();
|
|
1774
|
+
for (const key of Object.keys(expected) as (keyof typeof expected)[]) {
|
|
1775
|
+
expect([key, floor(m[key])]).toEqual([key, expected[key][i]]);
|
|
1776
|
+
}
|
|
1777
|
+
expect(Math.min(...Object.values(m))).toBeGreaterThanOrEqual(3);
|
|
1778
|
+
});
|
|
1779
|
+
});
|
|
1780
|
+
});
|
|
@@ -49,7 +49,8 @@ export type DescriptionListSeparator = 'line' | 'divider' | 'none';
|
|
|
49
49
|
* - `stacked` layout: term above value, for narrow columns.
|
|
50
50
|
* - Per-row value emphasis and optional links.
|
|
51
51
|
* - Semantic markup (`<dl>`/`<dt>`/`<dd>`) for accessibility; `clickable`
|
|
52
|
-
* rows
|
|
52
|
+
* rows hold a native button (named by term and value) that the keyboard
|
|
53
|
+
* reaches, and the `dl` keeps its term and value pairs.
|
|
53
54
|
*
|
|
54
55
|
* @example
|
|
55
56
|
* ```html
|
|
@@ -87,17 +88,28 @@ export declare class DescriptionListComponent {
|
|
|
87
88
|
*/
|
|
88
89
|
readonly separator: import("@angular/core").InputSignal<DescriptionListSeparator>;
|
|
89
90
|
/**
|
|
90
|
-
* Whether rows are interactive. When set, each row
|
|
91
|
-
*
|
|
92
|
-
* the
|
|
91
|
+
* Whether rows are interactive. When set, each row holds a native button,
|
|
92
|
+
* named by its term and value, that Tab reaches; Enter, Space and a click
|
|
93
|
+
* anywhere on the row emit `itemClick`. The `dl` keeps its term and value
|
|
94
|
+
* pairs. Set this whenever you handle `itemClick`.
|
|
93
95
|
* @default false
|
|
94
96
|
*/
|
|
95
97
|
readonly clickable: import("@angular/core").InputSignal<boolean>;
|
|
96
98
|
/** Emitted when a row is clicked (or activated via keyboard when `clickable`). */
|
|
97
99
|
readonly itemClick: import("@angular/core").OutputEmitterRef<DescriptionListItem>;
|
|
100
|
+
/** Generated id prefix, taken once per instance, so later ids don't shift. */
|
|
101
|
+
private readonly uid;
|
|
102
|
+
/** Id of row `index`'s term (only rendered when `clickable`). */
|
|
103
|
+
protected termId(index: number): string;
|
|
104
|
+
/** Id of row `index`'s value (only rendered when `clickable`). */
|
|
105
|
+
protected valueId(index: number): string;
|
|
98
106
|
protected readonly hostClasses: import("@angular/core").Signal<string>;
|
|
99
107
|
protected valueClass(item: DescriptionListItem): string;
|
|
100
|
-
/**
|
|
108
|
+
/**
|
|
109
|
+
* Keydown dispatched on the row itself (a consumer's test, a script). Keys
|
|
110
|
+
* from the row's button or a nested link are theirs: the button's native
|
|
111
|
+
* activation clicks it, and the click bubbles to the row.
|
|
112
|
+
*/
|
|
101
113
|
protected onRowKeydown(event: KeyboardEvent, item: DescriptionListItem): void;
|
|
102
114
|
static ɵfac: i0.ɵɵFactoryDeclaration<DescriptionListComponent, never>;
|
|
103
115
|
static ɵcmp: i0.ɵɵComponentDeclaration<DescriptionListComponent, "lc-description-list", never, {
|