bank20baht-ui 0.7.3 → 0.7.4
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/components/primitives/baht-heatmap.d.ts +13 -5
- package/dist/components/primitives/baht-heatmap.js +98 -93
- package/dist/components/primitives/baht-table.d.ts +58 -0
- package/dist/components/primitives/baht-table.js +295 -207
- package/dist/components/primitives/index.d.ts +1 -1
- package/dist/css/components.css +128 -31
- package/dist/css/tokens.css +3 -1
- package/dist/custom-elements.json +338 -4
- package/dist/tokens.css +131 -32
- package/dist/vscode.html-custom-data.json +22 -2
- package/dist/web-types.json +48 -3
- package/package.json +1 -1
|
@@ -9,11 +9,15 @@ import { BahtChartBase, ChartDatePoint } from './charts.js';
|
|
|
9
9
|
* el.values = [{ date: '2026-09-01', value: 420 }];
|
|
10
10
|
* el.from = '2026-09-01'; el.to = '2026-09-30';
|
|
11
11
|
*
|
|
12
|
-
* `show-values` swaps the compact GitHub-style weeks
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* `show-values` swaps the compact GitHub-style SVG (weeks as columns) for a
|
|
13
|
+
* month grid rendered as HTML (weeks as rows): seven weekday headers, one
|
|
14
|
+
* focusable button per day carrying the day number and the formatted value,
|
|
15
|
+
* today's cell outlined. Being HTML, the grid fills its container while text
|
|
16
|
+
* keeps its real size — nothing is scaled. Cell height follows
|
|
17
|
+
* `--baht-heatmap-cell-min` (56px). The two darkest ramp steps flip to light
|
|
18
|
+
* text.
|
|
15
19
|
*
|
|
16
|
-
* <baht-heatmap show-values format="currency" .values=${daily}></baht-heatmap>
|
|
20
|
+
* <baht-heatmap show-values format="currency" zero-label="none" .values=${daily}></baht-heatmap>
|
|
17
21
|
*/
|
|
18
22
|
export declare class BahtHeatmap extends BahtChartBase {
|
|
19
23
|
/** `{ date: 'YYYY-MM-DD', value }[]`; days with no entry render as empty. */
|
|
@@ -36,6 +40,10 @@ export declare class BahtHeatmap extends BahtChartBase {
|
|
|
36
40
|
protected render(): TemplateResult;
|
|
37
41
|
/** Compact GitHub-style layout: weeks as columns, weekdays as rows. */
|
|
38
42
|
private renderCompact;
|
|
39
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* `show-values` layout: an HTML month grid — weeks as rows, weekdays as
|
|
45
|
+
* columns, the day number and value printed in each cell. Buttons give
|
|
46
|
+
* every day focus and a role for free; the tooltip rides on focus/hover.
|
|
47
|
+
*/
|
|
40
48
|
private renderCalendar;
|
|
41
49
|
}
|
|
@@ -1,145 +1,150 @@
|
|
|
1
|
-
import { html as
|
|
2
|
-
import { property as m, customElement as
|
|
3
|
-
import { BahtChartBase as
|
|
4
|
-
var
|
|
5
|
-
for (var
|
|
6
|
-
(s =
|
|
7
|
-
return
|
|
1
|
+
import { html as u, nothing as T, svg as k } from "lit";
|
|
2
|
+
import { property as m, customElement as _ } from "lit/decorators.js";
|
|
3
|
+
import { BahtChartBase as z, chartSeqColor as x, renderTableTwin as A } from "./charts.js";
|
|
4
|
+
var O = Object.defineProperty, P = Object.getOwnPropertyDescriptor, c = (t, a, r, e) => {
|
|
5
|
+
for (var n = e > 1 ? void 0 : e ? P(a, r) : a, i = t.length - 1, s; i >= 0; i--)
|
|
6
|
+
(s = t[i]) && (n = (e ? s(a, r, n) : s(n)) || n);
|
|
7
|
+
return e && n && O(a, r, n), n;
|
|
8
8
|
};
|
|
9
|
-
const
|
|
10
|
-
function
|
|
11
|
-
const
|
|
12
|
-
return
|
|
9
|
+
const D = 22, y = 24, S = 34, C = 18, E = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
|
10
|
+
function M(t) {
|
|
11
|
+
const a = /^(\d{4})-(\d{2})-(\d{2})/.exec(t ?? "");
|
|
12
|
+
return a ? new Date(Date.UTC(+a[1], +a[2] - 1, +a[3])) : null;
|
|
13
13
|
}
|
|
14
|
-
function
|
|
15
|
-
return
|
|
14
|
+
function L(t) {
|
|
15
|
+
return t.toISOString().slice(0, 10);
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
function V() {
|
|
18
|
+
const t = /* @__PURE__ */ new Date(), a = String(t.getMonth() + 1).padStart(2, "0"), r = String(t.getDate()).padStart(2, "0");
|
|
19
|
+
return `${t.getFullYear()}-${a}-${r}`;
|
|
20
|
+
}
|
|
21
|
+
let p = class extends z {
|
|
18
22
|
constructor() {
|
|
19
23
|
super(...arguments), this.values = [], this.from = "", this.to = "", this.lessLabel = "Less", this.moreLabel = "More", this.zeroLabel = "—", this.showValues = !1, this.label = "";
|
|
20
24
|
}
|
|
21
25
|
get range() {
|
|
22
|
-
const
|
|
23
|
-
return !
|
|
26
|
+
const t = (this.values ?? []).map((e) => M(e.date)).filter((e) => !!e), a = M(this.from) ?? (t.length ? new Date(Math.min(...t.map((e) => +e))) : null), r = M(this.to) ?? (t.length ? new Date(Math.max(...t.map((e) => +e))) : null);
|
|
27
|
+
return !a || !r || +r < +a ? null : { start: a, end: r };
|
|
24
28
|
}
|
|
25
29
|
render() {
|
|
26
|
-
const
|
|
27
|
-
if (!
|
|
28
|
-
return
|
|
29
|
-
const
|
|
30
|
+
const t = this.hold({ values: this.values ?? [], range: this.range }), a = t.range;
|
|
31
|
+
if (!a || t.values.length === 0)
|
|
32
|
+
return u`<div class=${this.rootClass("baht-heatmap")}>${this.renderEmpty()}</div>`;
|
|
33
|
+
const r = new Map(t.values.map((l) => [l.date.slice(0, 10), l.value])), e = [], n = new Date(a.start), i = (a.start.getUTCDay() + 6) % 7;
|
|
30
34
|
let s = 0;
|
|
31
|
-
for (; +
|
|
32
|
-
const l =
|
|
33
|
-
|
|
35
|
+
for (; +n <= +a.end; ) {
|
|
36
|
+
const l = L(n), v = (n.getUTCDay() + 6) % 7, f = Math.floor((i + s) / 7);
|
|
37
|
+
e.push({ iso: l, value: r.has(l) ? r.get(l) : null, weekday: v, week: f }), n.setUTCDate(n.getUTCDate() + 1), s += 1;
|
|
34
38
|
}
|
|
35
|
-
const
|
|
36
|
-
return
|
|
37
|
-
${
|
|
39
|
+
const h = Math.max(1, ...e.map((l) => l.value ?? 0)), o = (l) => Math.min(4, Math.max(0, Math.ceil(l / h * 5) - 1)), b = e.reduce((l, v) => l + (v.value ?? 0), 0), $ = this.label || `Calendar heatmap, ${e.length} days from ${L(a.start)} to ${L(a.end)}, total ${this.fmt(b)}`, d = V(), g = this.showValues ? this.renderCalendar(e, o, d, $) : this.renderCompact(e, o, $);
|
|
40
|
+
return u`<div class=${this.rootClass("baht-heatmap")}>
|
|
41
|
+
${g}
|
|
38
42
|
${this.renderTooltip()}
|
|
39
43
|
<div class="baht-heatmap-scale">
|
|
40
44
|
<span class="baht-heatmap-scale-label">${this.lessLabel}</span>
|
|
41
45
|
${[0, 1, 2, 3, 4].map(
|
|
42
|
-
(l) =>
|
|
46
|
+
(l) => u`<span
|
|
43
47
|
class="baht-chart-swatch"
|
|
44
|
-
style=${`background:${
|
|
48
|
+
style=${`background:${x(l)}`}
|
|
45
49
|
aria-hidden="true"
|
|
46
50
|
></span>`
|
|
47
51
|
)}
|
|
48
52
|
<span class="baht-heatmap-scale-label">${this.moreLabel}</span>
|
|
49
53
|
</div>
|
|
50
|
-
${this.showTable ?
|
|
54
|
+
${this.showTable ? A(
|
|
51
55
|
this.label || "Calendar heatmap data",
|
|
52
56
|
["Date", "Value"],
|
|
53
|
-
|
|
54
|
-
) :
|
|
57
|
+
e.map((l) => [l.iso, l.value == null ? "—" : this.fmt(l.value)])
|
|
58
|
+
) : T}
|
|
55
59
|
</div>`;
|
|
56
60
|
}
|
|
57
61
|
/** Compact GitHub-style layout: weeks as columns, weekdays as rows. */
|
|
58
|
-
renderCompact(
|
|
59
|
-
const
|
|
60
|
-
return
|
|
62
|
+
renderCompact(t, a, r) {
|
|
63
|
+
const e = Math.max(1, ...t.map((s) => s.week + 1)), n = S + e * y, i = C + 7 * y + 4;
|
|
64
|
+
return u`<svg
|
|
61
65
|
class="baht-chart-svg baht-heatmap-svg"
|
|
62
|
-
width=${
|
|
63
|
-
height=${
|
|
64
|
-
viewBox="0 0 ${
|
|
66
|
+
width=${n}
|
|
67
|
+
height=${i}
|
|
68
|
+
viewBox="0 0 ${n} ${i}"
|
|
65
69
|
preserveAspectRatio="xMinYMin meet"
|
|
66
70
|
role="img"
|
|
67
|
-
aria-label=${
|
|
71
|
+
aria-label=${r}
|
|
68
72
|
>
|
|
69
|
-
${
|
|
70
|
-
(s,
|
|
73
|
+
${E.map(
|
|
74
|
+
(s, h) => h % 2 === 0 ? k`<text class="baht-chart-tick" x="0" y=${C + h * y + D / 2 + 4}>${s}</text>` : T
|
|
71
75
|
)}
|
|
72
|
-
${
|
|
73
|
-
const
|
|
74
|
-
return
|
|
75
|
-
width=${
|
|
76
|
-
@pointermove=${(
|
|
76
|
+
${t.map((s) => {
|
|
77
|
+
const h = s.value == null ? `${s.iso}: no activity` : `${s.iso}: ${this.fmt(s.value)}`, o = s.value == null || s.value <= 0 ? "var(--baht-chart-grid)" : x(a(s.value));
|
|
78
|
+
return k`<rect class="baht-heatmap-cell" x=${S + s.week * y} y=${C + s.weekday * y}
|
|
79
|
+
width=${D} height=${D} rx="3" fill=${o} tabindex="0" role="button" aria-label=${h}
|
|
80
|
+
@pointermove=${(b) => this.showTip(b, h)}
|
|
77
81
|
@pointerleave=${() => this.hideTip()}
|
|
78
|
-
@focus=${(
|
|
82
|
+
@focus=${(b) => this.showTip(b, h)}
|
|
79
83
|
@blur=${() => this.hideTip()}/>`;
|
|
80
84
|
})}
|
|
81
85
|
</svg>`;
|
|
82
86
|
}
|
|
83
|
-
/**
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
>
|
|
95
|
-
${
|
|
96
|
-
(
|
|
97
|
-
|
|
98
|
-
${o.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
class
|
|
102
|
-
|
|
103
|
-
|
|
87
|
+
/**
|
|
88
|
+
* `show-values` layout: an HTML month grid — weeks as rows, weekdays as
|
|
89
|
+
* columns, the day number and value printed in each cell. Buttons give
|
|
90
|
+
* every day focus and a role for free; the tooltip rides on focus/hover.
|
|
91
|
+
*/
|
|
92
|
+
renderCalendar(t, a, r, e) {
|
|
93
|
+
var h;
|
|
94
|
+
const n = ((h = t[0]) == null ? void 0 : h.weekday) ?? 0, i = t.length ? 6 - t[t.length - 1].weekday : 0, s = (o) => Array.from(
|
|
95
|
+
{ length: o },
|
|
96
|
+
() => u`<span class="baht-heatmap-day baht-heatmap-day--pad" aria-hidden="true"></span>`
|
|
97
|
+
);
|
|
98
|
+
return u`<div class="baht-heatmap-cal" role="group" aria-label=${e}>
|
|
99
|
+
${E.map((o) => u`<span class="baht-heatmap-weekday" aria-hidden="true">${o}</span>`)}
|
|
100
|
+
${s(n)}
|
|
101
|
+
${t.map((o) => {
|
|
102
|
+
const b = o.iso === r, $ = o.value == null || o.value <= 0, d = $ ? null : a(o.value), g = d != null && d >= 3, l = o.value == null ? `${o.iso}: no activity` : `${o.iso}: ${this.fmt(o.value)}`, v = +o.iso.slice(8, 10), f = $ ? this.zeroLabel : this.fmt(o.value), B = `baht-heatmap-day${b ? " baht-heatmap-day--today" : ""}${$ ? " baht-heatmap-day--zero" : ""}${g ? " baht-heatmap-day--dark" : ""}`, U = d == null ? "" : `--baht-heatmap-fill:${x(d)}`;
|
|
103
|
+
return u`<button
|
|
104
|
+
type="button"
|
|
105
|
+
class=${B}
|
|
106
|
+
style=${U || T}
|
|
107
|
+
aria-label=${l}
|
|
108
|
+
@pointermove=${(w) => this.showTip(w, l)}
|
|
104
109
|
@pointerleave=${() => this.hideTip()}
|
|
105
|
-
@focus=${(
|
|
110
|
+
@focus=${(w) => this.showTip(w, l)}
|
|
106
111
|
@blur=${() => this.hideTip()}
|
|
107
112
|
>
|
|
108
|
-
<
|
|
109
|
-
<
|
|
110
|
-
|
|
111
|
-
</g>`;
|
|
113
|
+
<span class="baht-heatmap-day-num">${v}</span>
|
|
114
|
+
<span class="baht-heatmap-day-value">${f}</span>
|
|
115
|
+
</button>`;
|
|
112
116
|
})}
|
|
113
|
-
|
|
117
|
+
${s(i)}
|
|
118
|
+
</div>`;
|
|
114
119
|
}
|
|
115
120
|
};
|
|
116
|
-
|
|
121
|
+
c([
|
|
117
122
|
m({ attribute: !1 })
|
|
118
|
-
],
|
|
119
|
-
|
|
123
|
+
], p.prototype, "values", 2);
|
|
124
|
+
c([
|
|
120
125
|
m()
|
|
121
|
-
],
|
|
122
|
-
|
|
126
|
+
], p.prototype, "from", 2);
|
|
127
|
+
c([
|
|
123
128
|
m()
|
|
124
|
-
],
|
|
125
|
-
|
|
129
|
+
], p.prototype, "to", 2);
|
|
130
|
+
c([
|
|
126
131
|
m({ attribute: "less-label" })
|
|
127
|
-
],
|
|
128
|
-
|
|
132
|
+
], p.prototype, "lessLabel", 2);
|
|
133
|
+
c([
|
|
129
134
|
m({ attribute: "more-label" })
|
|
130
|
-
],
|
|
131
|
-
|
|
135
|
+
], p.prototype, "moreLabel", 2);
|
|
136
|
+
c([
|
|
132
137
|
m({ attribute: "zero-label" })
|
|
133
|
-
],
|
|
134
|
-
|
|
138
|
+
], p.prototype, "zeroLabel", 2);
|
|
139
|
+
c([
|
|
135
140
|
m({ type: Boolean, attribute: "show-values" })
|
|
136
|
-
],
|
|
137
|
-
|
|
141
|
+
], p.prototype, "showValues", 2);
|
|
142
|
+
c([
|
|
138
143
|
m()
|
|
139
|
-
],
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
],
|
|
144
|
+
], p.prototype, "label", 2);
|
|
145
|
+
p = c([
|
|
146
|
+
_("baht-heatmap")
|
|
147
|
+
], p);
|
|
143
148
|
export {
|
|
144
|
-
|
|
149
|
+
p as BahtHeatmap
|
|
145
150
|
};
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { TemplateResult } from 'lit';
|
|
2
2
|
import { BahtElement } from '../base.js';
|
|
3
3
|
export type CellRenderer = (value: unknown, row: Record<string, unknown>) => unknown;
|
|
4
|
+
/** Group key for `groupBy`: a (dotted) row key, or a function of the row. */
|
|
5
|
+
export type GroupKey = string | ((row: Record<string, unknown>) => unknown);
|
|
6
|
+
/** Group header content: the group's value plus the rows in that group. */
|
|
7
|
+
export type GroupLabelRenderer = (value: unknown, rows: Record<string, unknown>[]) => unknown;
|
|
8
|
+
/** `baht-group-toggle` detail: which group and whether it is now collapsed. */
|
|
9
|
+
export type GroupToggleDetail = {
|
|
10
|
+
value: unknown;
|
|
11
|
+
collapsed: boolean;
|
|
12
|
+
};
|
|
4
13
|
export type ColumnAlign = 'start' | 'center' | 'end';
|
|
5
14
|
/**
|
|
6
15
|
* One column: a plain label, or a spec. A spec may carry `fields` (a
|
|
@@ -85,6 +94,25 @@ export type ColumnsChangeDetail = {
|
|
|
85
94
|
* t.sort = { key: 'amount', direction: 'desc' };
|
|
86
95
|
* t.addEventListener('baht-sort-change', (e) => refetch({ sort: e.detail }));
|
|
87
96
|
* t.rowActions = true; // trailing "..." button -> baht-row-actions { row, index, id }
|
|
97
|
+
*
|
|
98
|
+
* Grouping (ServiceNow "Group by" on a list): `group-by` names a (dotted)
|
|
99
|
+
* row key, or pass a function as the property. Every row on the CURRENT page
|
|
100
|
+
* is bucketed by that value in first-appearance order; each bucket is its own
|
|
101
|
+
* `<tbody>` headed by a `th[scope=rowgroup]` disclosure reading
|
|
102
|
+
* "Column: value (n)". Click the header to collapse/expand the group
|
|
103
|
+
* (`baht-group-toggle`); `group-collapsed` starts them all closed, and the
|
|
104
|
+
* open/closed state resets when `items`/`page` change. Grouping is per page
|
|
105
|
+
* only — it rides on the existing pagination and never fetches or sorts, so
|
|
106
|
+
* a value split across pages shows up on each. `groupLabel` replaces the
|
|
107
|
+
* default header text (it still sits inside the toggle). Row numbers and
|
|
108
|
+
* event indexes stay page-global. `hide-header` drops the column header row
|
|
109
|
+
* for list-style layouts.
|
|
110
|
+
*
|
|
111
|
+
* t.groupBy = 'state'; // "State: Open (12)"
|
|
112
|
+
* t.groupBy = (row) => row.occurred_at.slice(0, 10);
|
|
113
|
+
* t.groupLabel = (day, rows) => `${formatDay(day)} · ${rows.length}`;
|
|
114
|
+
* t.groupCollapsed = true;
|
|
115
|
+
* t.addEventListener('baht-group-toggle', (e) => console.log(e.detail)); // { value, collapsed }
|
|
88
116
|
*/
|
|
89
117
|
export declare class BahtTable extends BahtElement {
|
|
90
118
|
/** Rows of the CURRENT page. */
|
|
@@ -107,6 +135,16 @@ export declare class BahtTable extends BahtElement {
|
|
|
107
135
|
renderers: Record<string, CellRenderer>;
|
|
108
136
|
/** Subset + order of columns to render (dotted keys allowed); empty/omitted = show all top-level columns. */
|
|
109
137
|
visibleColumns: string[];
|
|
138
|
+
/** Group the page's rows by this (dotted) column key — or a function of the row (property). Unset = no grouping. */
|
|
139
|
+
groupBy?: GroupKey;
|
|
140
|
+
/** Group header content (property); default is "Column: value (n)". */
|
|
141
|
+
groupLabel?: GroupLabelRenderer;
|
|
142
|
+
/** Group headers are disclosures (click to collapse). Set the JS property to `false` for static headers. */
|
|
143
|
+
groupCollapsible: boolean;
|
|
144
|
+
/** Start every group collapsed (per page — resets when `items`/`page` change). */
|
|
145
|
+
groupCollapsed: boolean;
|
|
146
|
+
/** Drop the column header row (list-style tables, e.g. grouped ledgers). */
|
|
147
|
+
hideHeader: boolean;
|
|
110
148
|
/** Built-in "Columns" toggle — derives its options from `columns`/rows. Set `column-picker="false"` to hide it. */
|
|
111
149
|
columnPicker: boolean;
|
|
112
150
|
/** Total matching rows across all pages — renders "{n} results" in the toolbar. Unset = no count. */
|
|
@@ -136,6 +174,8 @@ export declare class BahtTable extends BahtElement {
|
|
|
136
174
|
private pickSel;
|
|
137
175
|
/** Reference columns unfolded in the Available tree (dot-walk). */
|
|
138
176
|
private expanded;
|
|
177
|
+
/** Group keys the person toggled away from `groupCollapsed`; reset on a new page. */
|
|
178
|
+
private toggledGroups;
|
|
139
179
|
/** Element focused before the picker opened — focus returns there on close. */
|
|
140
180
|
private pickerPreviousFocus;
|
|
141
181
|
private readonly onPickerKeydown;
|
|
@@ -183,7 +223,23 @@ export declare class BahtTable extends BahtElement {
|
|
|
183
223
|
/** Alignment class for a column (explicit `align` beats `numeric`). */
|
|
184
224
|
private alignClass;
|
|
185
225
|
private cell;
|
|
226
|
+
private span;
|
|
227
|
+
private groupValue;
|
|
228
|
+
/** Stable identity of a group value for the collapsed set (objects by JSON, else by type+string). */
|
|
229
|
+
private static groupId;
|
|
230
|
+
/**
|
|
231
|
+
* The page's rows bucketed by group value, in first-appearance order. Each
|
|
232
|
+
* row keeps its page index so numbering and event indexes stay host-order.
|
|
233
|
+
*/
|
|
234
|
+
private get groups();
|
|
235
|
+
private get isGrouped();
|
|
236
|
+
private isCollapsed;
|
|
237
|
+
private toggleGroup;
|
|
238
|
+
/** Default header: "Column: value (n)" — no column prefix for a function key. */
|
|
239
|
+
private defaultGroupLabel;
|
|
240
|
+
private renderGroups;
|
|
186
241
|
private renderBody;
|
|
242
|
+
private renderRow;
|
|
187
243
|
private renderHeaderCell;
|
|
188
244
|
private get hasToolbar();
|
|
189
245
|
private renderToolbar;
|
|
@@ -196,4 +252,6 @@ export declare class BahtTable extends BahtElement {
|
|
|
196
252
|
private renderAvailRow;
|
|
197
253
|
private renderColumnModal;
|
|
198
254
|
protected render(): TemplateResult;
|
|
255
|
+
private renderHead;
|
|
256
|
+
private renderFooter;
|
|
199
257
|
}
|