@columenlabs/lumen-ui 0.1.108 → 0.1.109
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/dist/coverage-map.js +22 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/ink.d.ts +7 -0
- package/dist/ink.js +39 -0
- package/package.json +2 -2
package/dist/coverage-map.js
CHANGED
|
@@ -246,7 +246,7 @@ export function LumenCoverageMap({ source, title, icon = Grid3x3Icon, rows: rowS
|
|
|
246
246
|
useLayoutEffect(() => {
|
|
247
247
|
const el = scroller.current;
|
|
248
248
|
if (el && layout === 'grid')
|
|
249
|
-
el
|
|
249
|
+
snapToEnd(el);
|
|
250
250
|
edge.update();
|
|
251
251
|
}, [strip]);
|
|
252
252
|
const sums = mobile ? summary.filter((s) => s === 'complete') : summary;
|
|
@@ -476,7 +476,7 @@ export function LumenCoverageMap({ source, title, icon = Grid3x3Icon, rows: rowS
|
|
|
476
476
|
link: rowSpec.link,
|
|
477
477
|
};
|
|
478
478
|
let group;
|
|
479
|
-
body = (_jsxs("div", { className: cn('grid min-w-min items-center gap-[3px]', fade), style: grid, ...routes, children: [!mobile && (_jsx("span", { className: "sticky left-0 z-1 bg-card text-xs text-muted-foreground", children: rowSpec.title })), cols.map((k) => (_jsx("span", { className: "num h-4 overflow-visible text-[11px] whitespace-nowrap text-muted-foreground", children: headLabel(k, block, cols.length) }, k))), sums.map((s) => (_jsx("span", { className: "pl-3 text-right text-xs text-muted-foreground", children: s === 'rows' || s === 'counts' ? rowsHead : SUMMARY_HEAD[s] }, s))), rowList.flatMap((r) => {
|
|
479
|
+
body = (_jsxs("div", { className: cn('grid min-w-min items-center gap-[3px]', fade), style: grid, ...routes, children: [!mobile && (_jsx("span", { className: "sticky left-0 z-1 bg-card text-xs text-muted-foreground", children: rowSpec.title })), cols.map((k) => (_jsx("span", { "data-col": "", className: "num h-4 overflow-visible text-[11px] whitespace-nowrap text-muted-foreground", children: headLabel(k, block, cols.length) }, k))), sums.map((s) => (_jsx("span", { className: "pl-3 text-right text-xs text-muted-foreground", children: s === 'rows' || s === 'counts' ? rowsHead : SUMMARY_HEAD[s] }, s))), rowList.flatMap((r) => {
|
|
480
480
|
const out = [];
|
|
481
481
|
const shut = groupBy && folds.has(foldKey + (r.group ?? ''));
|
|
482
482
|
if (groupBy && r.group !== group) {
|
|
@@ -567,3 +567,23 @@ export function LumenCoverageMap({ source, title, icon = Grid3x3Icon, rows: rowS
|
|
|
567
567
|
? 'Click a block to see its days'
|
|
568
568
|
: undefined) }) })] }) }) }) }));
|
|
569
569
|
}
|
|
570
|
+
/** Scrolls a sideways strip to its end (today) so its left edge falls on a whole block, not mid
|
|
571
|
+
* block with its label cut: the strip's content takes just enough room at its end to land there. */
|
|
572
|
+
function snapToEnd(el) {
|
|
573
|
+
const content = el.firstElementChild;
|
|
574
|
+
if (!content)
|
|
575
|
+
return;
|
|
576
|
+
content.style.paddingRight = '';
|
|
577
|
+
el.scrollLeft = el.scrollWidth;
|
|
578
|
+
const box = el.getBoundingClientRect().left;
|
|
579
|
+
const lefts = [...el.querySelectorAll('[data-col]')].map((c) => c.getBoundingClientRect().left - box);
|
|
580
|
+
// The first block cut by the left edge; the strip moves on to the block after it.
|
|
581
|
+
const cut = lefts.find((x, i) => x < -0.5 && (lefts[i + 1] ?? 1) > 0.5);
|
|
582
|
+
if (cut === undefined)
|
|
583
|
+
return;
|
|
584
|
+
const next = lefts[lefts.indexOf(cut) + 1];
|
|
585
|
+
if (next === undefined || next <= 0.5)
|
|
586
|
+
return;
|
|
587
|
+
content.style.paddingRight = `${Math.ceil(next)}px`;
|
|
588
|
+
el.scrollLeft = el.scrollWidth;
|
|
589
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export { RankedBars, StackedBars, StackedColumns, TimeLines } from './lumen-widg
|
|
|
28
28
|
export { type ShareSegment, ShareMeter } from './share-meter.js';
|
|
29
29
|
export { type Pool, poolShare } from './pool-share.js';
|
|
30
30
|
export { METER_LEVEL_COLOR, meterLevel, meterValueTone } from './meter-level.js';
|
|
31
|
+
export { contrast, readableInk } from './ink.js';
|
|
31
32
|
export { UnitValue } from './unit-value.js';
|
|
32
33
|
export { type BurnRate, burnRate, durationText, PACE_WINDOWS, type Pace, type PaceWindow, paceHours, setDefaultPaceWindow, timeToCap, usePaceWindow, } from './pace.js';
|
|
33
34
|
export { PaceWindowPicker, paceText, TimeToCap } from './time-to-cap.js';
|
package/dist/index.js
CHANGED
|
@@ -35,6 +35,7 @@ export { RankedBars, StackedBars, StackedColumns, TimeLines } from './lumen-widg
|
|
|
35
35
|
export { ShareMeter } from './share-meter.js';
|
|
36
36
|
export { poolShare } from './pool-share.js';
|
|
37
37
|
export { METER_LEVEL_COLOR, meterLevel, meterValueTone } from './meter-level.js';
|
|
38
|
+
export { contrast, readableInk } from './ink.js';
|
|
38
39
|
export { UnitValue } from './unit-value.js';
|
|
39
40
|
export { burnRate, durationText, PACE_WINDOWS, paceHours, setDefaultPaceWindow, timeToCap, usePaceWindow, } from './pace.js';
|
|
40
41
|
export { PaceWindowPicker, paceText, TimeToCap } from './time-to-cap.js';
|
package/dist/ink.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** WCAG contrast of two hex colours, 1 to 21; null when either is not a hex colour. */
|
|
2
|
+
export declare function contrast(a: string, b: string): number | null;
|
|
3
|
+
/**
|
|
4
|
+
* The text colour for a chip of background `bg`: its own `ink` when that reads at `min`:1 (WCAG AA,
|
|
5
|
+
* 4.5), otherwise near-black or white, whichever reads better. A colour that is not hex keeps `ink`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function readableInk(bg: string, ink?: string, min?: number): string | undefined;
|
package/dist/ink.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Text that stays readable on a colour the app does not choose (a mailbox's own label colours).
|
|
2
|
+
const hex = (c) => {
|
|
3
|
+
const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(c.trim());
|
|
4
|
+
const d = m?.[1];
|
|
5
|
+
if (!d)
|
|
6
|
+
return null;
|
|
7
|
+
const h = d.length === 3 ? [...d].map((x) => x + x).join('') : d;
|
|
8
|
+
return [0, 2, 4].map((i) => Number.parseInt(h.slice(i, i + 2), 16));
|
|
9
|
+
};
|
|
10
|
+
const luminance = ([r, g, b]) => {
|
|
11
|
+
const lin = (v) => {
|
|
12
|
+
const s = v / 255;
|
|
13
|
+
return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
|
|
14
|
+
};
|
|
15
|
+
return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
|
|
16
|
+
};
|
|
17
|
+
/** WCAG contrast of two hex colours, 1 to 21; null when either is not a hex colour. */
|
|
18
|
+
export function contrast(a, b) {
|
|
19
|
+
const x = hex(a);
|
|
20
|
+
const y = hex(b);
|
|
21
|
+
if (!x || !y)
|
|
22
|
+
return null;
|
|
23
|
+
const p = luminance(x);
|
|
24
|
+
const q = luminance(y);
|
|
25
|
+
return (Math.max(p, q) + 0.05) / (Math.min(p, q) + 0.05);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The text colour for a chip of background `bg`: its own `ink` when that reads at `min`:1 (WCAG AA,
|
|
29
|
+
* 4.5), otherwise near-black or white, whichever reads better. A colour that is not hex keeps `ink`.
|
|
30
|
+
*/
|
|
31
|
+
export function readableInk(bg, ink, min = 4.5) {
|
|
32
|
+
if (ink && (contrast(bg, ink) ?? min) >= min)
|
|
33
|
+
return ink;
|
|
34
|
+
const dark = contrast(bg, '#111111');
|
|
35
|
+
const light = contrast(bg, '#ffffff');
|
|
36
|
+
if (dark === null || light === null)
|
|
37
|
+
return ink;
|
|
38
|
+
return dark >= light ? '#111111' : '#ffffff';
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.109",
|
|
4
4
|
"description": "Lumen: shared React components (frame, views, widgets, charts, drawers).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"@codemirror/lang-javascript": "6.2.5",
|
|
50
50
|
"@codemirror/lang-json": "6.0.2",
|
|
51
51
|
"@codemirror/lang-sql": "6.10.0",
|
|
52
|
-
"@columenlabs/lumen-ui-types": "0.1.
|
|
52
|
+
"@columenlabs/lumen-ui-types": "0.1.109",
|
|
53
53
|
"@dnd-kit/core": "6.3.1",
|
|
54
54
|
"@dnd-kit/sortable": "10.0.0",
|
|
55
55
|
"@dnd-kit/utilities": "3.2.2",
|